TypeScript中定义Constructor<T>约束构造函数参数时的类型校验异常问题
这不是TypeScript的Bug,而是因为你当前的Constructor<T>类型定义没有严格限制构造函数的参数个数,再结合TypeScript对类隐式构造函数的类型处理逻辑,才出现了这个情况。
为什么当前代码没有报错?
当你不给类定义显式构造函数时,TypeScript会自动生成一个隐式构造函数,它的类型被推断为new (...args: any[]) => T——简单来说,这个构造函数可以接受任意数量、任意类型的参数(毕竟在JavaScript里,调用构造函数时传入多余参数本来就是合法的,只是会被构造函数忽略)。
你的原Constructor<T>类型定义是:
type Constructor<T> = new (struct: Struct<T>) => T;
这个类型只要求构造函数能接收一个Struct<T>类型的参数,但new (...args: any[]) => T完全满足这个条件(你可以传入一个Struct<T>参数,构造函数大不了忽略它)。所以TypeScript认为typeof MagicMonkey和Constructor<MagicMonkey>是兼容的,自然不会报错。
你提到的“类型定义能强制函数内部写new Constructor(struct)而非new Constructor()”,是因为在createObjectIntance函数内部,TypeScript是基于Constructor<T>的类型做检查的——这个类型明确要求构造函数需要一个参数,所以写new Constructor()会触发类型错误,这部分逻辑是符合预期的。
如何让TypeScript强制要求构造函数必须显式接收Struct<T>参数?
要解决这个问题,你需要修改Constructor<T>的定义,让它严格限制构造函数的参数列表恰好只有一个Struct<T>类型的参数,而不是允许任意参数。可以通过元组类型来实现:
type PropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]; type Struct<T> = Pick<T, PropertyNames<T>>; // 使用元组类型强制参数个数为1,且类型必须是Struct<T> type Constructor<T> = new (...args: [Struct<T>]) => T; class MagicMonkey { age: number; name: string; fly() { } // 现在必须显式定义符合要求的构造函数,否则调用createObjectIntance会报错 constructor(struct: Struct<MagicMonkey>) { this.age = struct.age; this.name = struct.name; } } function createObjectIntance<T>(Constructor: Constructor<T>, struct: Struct<T>): T { return new Constructor(struct); } // 现在正常工作 const jack: MagicMonkey = createObjectIntance(MagicMonkey, { name: 'jack', age: 1000}) console.log(jack)
现在,如果MagicMonkey没有显式定义接收Struct<MagicMonkey>的构造函数,TypeScript会直接抛出类型错误,完全符合你的预期。
内容的提问来源于stack exchange,提问作者Marthinus Engelbrecht

