UNPKG

egreact

Version:

A react render for egret 一个为 egret 而生的 react 渲染器

177 lines (164 loc) 6.29 kB
import { IPropsHandlers, IPropInterface, ExtensionObj } from '../type' import { CONSTANTS } from '../constants' import { is, isEvent } from '../utils' import { EventProp, NormalProp } from './common' type ToUnionOfFunction<T> = T extends any ? (x: T) => any : never // 联合类型转交叉类型 export type UnionToIntersection<T> = ToUnionOfFunction<T> extends (x: infer P) => any ? P : never export type LiteralObj<T> = T extends object ? { [K in keyof T]: T[K] } : never module Mixin { /* 混入函数的类型 */ // 转换 P-__setter 的键为 P type TranslateHandlerKey<T extends object> = { [K in keyof T as K extends `${infer P}-__setter` ? P : K]: T[K] } // 转换 P-__diff 的键为 `${CONSTANTS.CUSTOM_DIFF_PREFIX}${P}` type TranslateDiffKey<T extends object> = { [K in keyof T as K extends `${infer P}-__diff` ? `${typeof CONSTANTS.CUSTOM_DIFF_PREFIX}${P}` : K]: T[K] } // 铺平对象,比如 {a:{b:string},b:number} 会转换为 { a-b:string,b:number } type _FlattenObject<T extends IPropInterface, S extends string> = { [K in Exclude<keyof T, Symbol>]: T[K] extends IPropInterface ? _FlattenObject<T[K], `${S}${K}-`> : { [_ in `${S}${K}`]: T[K] } }[Exclude<keyof T, Symbol>] // 给所有键加上前缀 type WrapKey<T, S extends string> = { [K in Exclude<keyof T, Symbol> as `${S}-${K}`]: T[K] } export type FlattenObject<T extends IPropInterface, K extends string> = TranslateDiffKey< TranslateHandlerKey<WrapKey<LiteralObj<UnionToIntersection<_FlattenObject<T, ''>>>, K>> > // 完成上述类型相对应的函数转换 // S extends [string] 用于字面量推断,传入的字符串会推断为字面量而非 string export const mixin = <T, P extends IPropInterface, S extends [string]>(target: T, obj: P, ...key: S) => { const entries: [string[], any][] = [] // 铺平收集键值对 const collectTranslateKey = (obj: P, prefixKey: string[]) => { for (const [key, value] of Object.entries(obj)) { if ('__setter' in value) { collectTranslateKey(value, [...prefixKey, key]) } else { entries.push([[...prefixKey, key], value]) } } } collectTranslateKey(obj, key) // 将键值对的键转换为连字符 const flattenObj = entries.reduce((obj, [keys, value]) => { if (keys[keys.length - 1] === '__setter') { keys.pop() obj[keys.join('-')] = value } else if (keys[keys.length - 1] === '__diff') { keys.pop() obj[CONSTANTS.CUSTOM_DIFF_PREFIX + keys.join('-')] = value } else { obj[keys.join('-')] = value } return obj }, {} as ExtensionObj) as Mixin.FlattenObject<P, S[0]> return { ...target, ...flattenObj } } } // 此工具转为链式调用写法更加清晰 export const mixinHelper = { store: {}, set<P extends { store: any }, T>(this: P, target: T) { return { ...this, store: target, } }, mixin<P extends { store: any }, T extends IPropInterface, S extends [string]>(this: P, obj: T, ...name: S) { type Store = P extends { store: infer D } ? D : never type Name = S extends [infer N] ? N : never return { ...this, store: Mixin.mixin(this.store as Store, obj as T, name[0] as Name), } }, get<P extends { store: any }>(this: P) { type Store = P extends { store: infer D } ? D : never return this.store as Store }, } // 用于有 __target 实例的代理 export const proxyHelper = <T extends new (...args: any[]) => any>(config: { constructor: T // 类 targetKey?: string // 可以用.分隔 excludeKeys?: string[] // 排除判断的键 setCallback?: (props: { instance: any; target: any; value: any; oldValue: any; propName: string | symbol }) => void // set 后的回调函数 configs?: ProxyHandler<InstanceType<T>> // 其他 proxy 配置 }) => { let { constructor, targetKey = '__target', excludeKeys = ['__renderInfo'], setCallback = () => void 0, configs = {}, } = config excludeKeys = [...excludeKeys, '__renderInfo'] const keys = targetKey.split('.') targetKey = keys.pop()! const name = 'Proxy' + constructor.name let proxyConstructor = { [name]: function (...args: any[]) { const instance = new constructor(...args) instance['$name'] = name return new Proxy(instance, { set(target, p, value) { // 优先设置原实例 if (p in target || excludeKeys.includes(p as string)) { const oldValue = target[p] target[p] = value setCallback.call(target, { instance: target, target: target, propName: p, value, oldValue, }) } else { // 否则设置内置实例 const _target = target target = keys.reduce((t, p) => t[p], target) const oldValue = target[targetKey][p] target[targetKey][p] = value setCallback.call(_target, { instance: _target, target: target[targetKey], propName: p, value, oldValue, }) } return true }, get(target, p) { // 如果原实例有优先返回原实例的 if (p in target) return target[p] else { // 否则返回内置实例的 target = keys.reduce((t, p) => t[p], target) return target[targetKey][p] } }, // 用于 instanceof 判断,否则会返回 proxy 的原型导致 instanceof 判断失败 getPrototypeOf() { return proxyConstructor.prototype }, ...configs, }) }, // 返回一个新类,它的实例是经过 proxy 处理的 // return }[name] as unknown as T return proxyConstructor } export function proxyGetPropsHandlers(target: ExtensionObj, key: symbol | string, receiver: any) { if (key in target) return Reflect.get(target, key, receiver) else if (typeof key === 'symbol' || key.startsWith('__')) return undefined else if (isEvent(key)) return EventProp.eventSetter else return NormalProp.pass } export type Cover<T, S> = Omit<T, keyof S> & S