Typescript 中没有反思或反省的概念.因此,一种方法是将元数据添加到您类型中,并使用您自己的Assign版本.
const MAPPED_TYPE_METADATA_KEY = Symbol('mappedType');
type TypeOfType = 'string' | 'number' | 'bigint' | 'boolean' | 'function' | 'object';
/** Decorator that adds mapped types to an object. */
function mappedType(typeName: TypeOfType) {
return function(target: any, propertyKey: string): void {
const typeMap = { ...Reflect.get(target, MAPPED_TYPE_METADATA_KEY), [propertyKey]: typeName };
Reflect.set(target, MAPPED_TYPE_METADATA_KEY, typeMap);
Reflect.defineMetadata(MAPPED_TYPE_METADATA_KEY, typeMap, target);
};
}
/** Uses mappedTypes to assign values to an object. */
function customAssign<T>(obj: T, data: Partial<{ [key in keyof A]: A[key] }>): void {
const typeMap: Record<string | number | symbol, TypeOfType> | undefined = Reflect.get(obj, MAPPED_TYPE_METADATA_KEY);
if (typeMap) {
Object.entries(data)
.filter(([key, value]) => typeMap[key as keyof T] === typeof value)
.forEach(([key, value]) => (obj as any)[key] = value);
}
}
class A {
@mappedType('string')
foo?: string;
@mappedType('number')
another: number = 1; // added for testing
constructor(data: any) {
customAssign(this, data);
}
}
const instance = new A({
foo: 'test',
bar: 'other'
});
console.log(instance); // outputs {another: 1, foo: 'test'} // another defaults to 1 and not accidentally overwritten.
该解决方案包括以下功能.
- 一个mapepdType的修饰符,它创建一个类型映射,该类型映射以字段名为键,并映射到值的类型.这里的危险在于,没有任何强制类型匹配的内容.
- 一个customAssign函数,其作用类似于ASSIGN,但会 destruct 由修饰者创建的a _typeMap字段,以便正确赋值.
- 构造函数中的Object.assign被替换为customAssign.
输出与您所需的输出相匹配.此外,默认情况下分配的值不会被覆盖.