TypeScript泛型创建对象报错:typeof X无法赋值给构造签名类型
我想要实现一个带有泛型类型<T>的函数,用来创建该类型的对象。原本打算用**构造签名(construct signatures)加上泛型约束(generic constraint)**限定类必须具备构造函数,但示例1的写法无法正常运行;换成直接把函数参数的类型定义为构造签名而非泛型约束(示例2),就能正常执行。
我始终搞不懂示例1报错的原因,查资料时看到类的**静态侧(static side)和实例侧(instance side)**的概念,但不清楚这和我的问题有什么关系。我曾猜测是不是type描述的是<T>的实例而非类本身,但同事给出的示例3却能正常运行,希望有人能解惑。
示例1(无法运行)
调用create(Person)时会报错:Argument of type 'typeof Person' is not assignable to parameter of type 'new (...args: any[]) => typeof Person'。
class Person { public name: string constructor(name: string = '') { this.name = name } } function create<T extends { new(...args:any[]) : T }> (type : T) : T { return new type() } console.log(create(Person))
示例2(可运行)
class Person { public name: string constructor(name: string = '') { this.name = name } } function create<T> (type : { new(...args:any[]) : T } ) : T { // 此处修改了类型定义 return new type() } console.log(create(Person))
示例3(可运行)
class Person { public name: string constructor(name: string = '') { this.name = name } } function create<T extends { new(...args:any[]) : any }> (type : T) : InstanceType<T> { // 此处修改了返回值类型 return new type() } console.log(create(Person))
问题原因解析
示例1的核心矛盾
你在示例1里的泛型约束T extends { new(...args:any[]) : T }犯了逻辑错误:这里的T同时被用来指代构造函数本身的类型(也就是类的静态侧typeof Person),又要求这个构造函数返回的实例类型也是T。但实际上,typeof Person(构造函数)调用后返回的是Person类型的实例(类的实例侧),两者完全不是一回事,所以TypeScript会判定类型不匹配,抛出错误。
示例2为什么能运行
示例2里的泛型T明确指代的是实例类型(比如Person),而参数type的类型被定义为“能返回T类型实例的构造签名”。当你传入Person(也就是typeof Person类型)时,TypeScript会自动推断T为Person,构造函数返回的实例正好是Person类型,完全符合函数的类型定义,所以能正常运行。
示例3的逻辑合理性
示例3里的泛型T指代的是构造函数类型(通过约束限定为任意能创建实例的构造函数),而返回值用InstanceType<T>这个内置工具类型,专门用来获取构造函数T对应的实例类型。当传入typeof Person时,InstanceType<T>会被解析为Person,new type()返回的实例正好匹配这个类型,因此不会报错。
静态侧和实例侧的关联
类的静态侧指的是构造函数本身的类型(typeof Class),包含类的静态属性、静态方法;实例侧指的是类实例的类型(Class),包含实例的属性和方法。示例1的错误本质就是混淆了这两个概念:要求构造函数返回静态侧类型,但实际构造函数只能返回实例侧类型,自然类型不兼容。
内容的提问来源于stack exchange,提问作者Markus Weninger

