如何用TypeScript为基于对象描述生成类的generator函数添加类型定义?
如何为基于对象描述生成类的函数编写TypeScript类型定义?
问题场景
现有一个generator函数,接收对象形式的类原型描述,转换后返回对应的类。但当前TypeScript无法正确推断生成类的实例与静态成员类型,导致使用时出现类型报错。需要为该函数编写合适的类型定义,实现完整的类型校验。
代码示例
function generator(prototype) { const cls = class { }; if (prototype.$name) { cls.name = prototype.$name; } // 原型转换逻辑(省略具体实现) cls.prototype = prototype; return cls; } const MyClass = generator({ f1: '2', _f2: { $default: '' }, f2: { get() { return this._f2; }, // this 被推断为 any set(v) { this._f2 = v; } }, f3: { $type: String, $default: 'asd', get() { return this.f1 + this.f2; }, set(v) { this.f1 = v[0]; this.f2 = v[1]; } }, m() { return this.f3; }, $static: { s1: 'static', s2: { $def: 'static 2' }, s3: { get() { return 'static 3'; } } } }); // 以下均会触发类型报错:属性不存在于类型 typeof cls/cls console.log(MyClass.s1); console.log(MyClass.s2); console.log(MyClass.s3); const o = new MyClass(); console.log(o.f1); console.log(o.f2); console.log(o.f3); console.log(o.m());
解决方案
通过高级泛型+条件类型解析输入的原型描述,建立输入与输出类之间的类型映射关系,让TypeScript正确推断实例和静态成员类型。
1. 定义原型描述的基础类型
先明确输入对象中各类成员的结构类型:
// 属性描述类型:支持直接值、带默认值/类型的配置、访问器 type PropertyDescriptor<T = any> = | T | { $default?: T; $type?: new () => T } | { get(): T; set?(v: T): void }; // 方法类型:约束this指向实例 type InstanceMethod<TInstance> = (this: TInstance, ...args: any[]) => any; // 静态成员描述类型 type StaticDescriptor<T = any> = | T | { $def?: T } | { get(): T }; // 完整的原型输入类型 type PrototypeInput<TInstance, TStatic> = { $name?: string; $static?: { [K in keyof TStatic]: StaticDescriptor<TStatic[K]> }; } & { [K in keyof TInstance]: TInstance[K] extends Function ? InstanceMethod<TInstance> : PropertyDescriptor<TInstance[K]>; };
2. 编写类型转换工具
创建工具类型,将输入的描述转换为实际的实例/静态类型:
// 从属性描述中提取实际类型 type ExtractPropertyType<D> = D extends { get(): infer T } ? T : D extends { $default: infer T } ? T : D extends { $type: new () => infer T } ? T : D extends any ? D : never; // 从静态描述中提取实际类型 type ExtractStaticType<D> = D extends { get(): infer T } ? T : D extends { $def: infer T } ? T : D extends any ? D : never; // 转换原型输入为实例类型 type InstanceTypeFromInput<Input> = { [K in keyof Input as Exclude<K, '$name' | '$static'>]: Input[K] extends InstanceMethod<any> ? Input[K] : ExtractPropertyType<Input[K]>; }; // 转换静态输入为静态类型 type StaticTypeFromInput<Input> = Input extends { $static: infer S } ? { [K in keyof S]: ExtractStaticType<S[K]>; } : {};
3. 为generator函数添加泛型注解
组合上述工具类型,为函数定义完整的类型约束:
function generator< TInput extends PrototypeInput<TInstance, TStatic>, TInstance = InstanceTypeFromInput<TInput>, TStatic = StaticTypeFromInput<TInput> >(prototype: TInput): (new () => TInstance) & TStatic { const cls = class { } as any; if (prototype.$name) { cls.name = prototype.$name; } // 保留你的原型转换逻辑 cls.prototype = prototype; // 处理静态成员挂载 if (prototype.$static) { Object.assign(cls, prototype.$static); } return cls; }
4. 验证效果
此时使用generator生成类时,TypeScript会自动推断所有成员的类型,不再出现报错:
const MyClass = generator({ f1: '2', _f2: { $default: '' }, f2: { get() { return this._f2; }, set(v) { this._f2 = v; } }, f3: { $type: String, $default: 'asd', get() { return this.f1 + this.f2; }, set(v) { this.f1 = v[0]; this.f2 = v[1]; } }, m() { return this.f3; }, $static: { s1: 'static', s2: { $def: 'static 2' }, s3: { get() { return 'static 3'; } } } }); // 静态成员类型正确识别 console.log(MyClass.s1); // 类型:string console.log(MyClass.s2); // 类型:string console.log(MyClass.s3); // 类型:string const o = new MyClass(); // 实例成员类型正确识别 console.log(o.f1); // 类型:string console.log(o.f2); // 类型:string console.log(o.f3); // 类型:string console.log(o.m()); // 类型:string
内容的提问来源于stack exchange,提问作者vadimmos
相关产品推荐
相关产品推荐

