如何在泛型函数中复用泛型静态方法的参数类型?
问题描述
我尝试在泛型函数中复用Omit<T, 'id'>类型,编写了如下TypeScript代码,但createModel的第二个参数类型无法正确识别子类的额外属性:
class Model{ static create<T extends Model>(a: Omit<T, 'id'>): number{ return 0; } id = 0; title = 'test'; } class Child extends Model{ child = true; } function createModel<T extends typeof Model>(model: T, data: Parameters<T['create']>[0]) : InstanceType<T> { return {} as InstanceType<T>; } const instance = createModel(Child, {title: 'test', child :true }); // 此处第二个参数的child属性无法被正确识别
尝试过Parameters<T['create']<InstanceType<T>>>等写法,但出现语法错误:
Cannot access 'T.func' because 'T' is a type, but not a namespace. Did you mean to retrieve the type of the property 'func' in 'T' with 'T["func"]'?(2713)
Parameter 'InstanceType' implicitly has an 'any' type.
请问是否有办法在不引入外部公共类型的情况下复用该参数类型?
解决方案
方案1:利用静态方法的this类型绑定实例类型
修改父类Model的create静态方法,通过this类型获取当前构造函数的实例类型,替代原有的泛型参数,这样子类继承的create方法会自动适配自身的实例类型:
class Model { // 使用this类型绑定当前构造函数,自动推导实例类型 static create(this: typeof Model, a: Omit<InstanceType<typeof this>, 'id'>): number { return 0; } id = 0; title = 'test'; } class Child extends Model { child = true; } function createModel<T extends typeof Model>(model: T, data: Parameters<T['create']>[0]): InstanceType<T> { return {} as InstanceType<T>; } // 现在child属性会被正确识别,类型检查通过 const instance = createModel(Child, { title: 'test', child: true });
核心逻辑:
- 静态方法中的
typeof this指向当前类的构造函数类型 InstanceType<typeof this>会自动推导为当前类的实例类型- 子类继承
create方法时,this类型会自动替换为子类的构造函数类型,参数类型也随之变为Omit<子类实例类型, 'id'>
方案2:直接推导实例类型的Omit类型
如果不想修改Model的create方法,可以直接在createModel中用Omit<InstanceType<T>, 'id'>作为第二个参数的类型,虽然重复了Omit逻辑,但无需额外类型定义:
class Model{ static create<T extends Model>(a: Omit<T, 'id'>): number{ return 0; } id = 0; title = 'test'; } class Child extends Model{ child = true; } function createModel<T extends typeof Model>(model: T, data: Omit<InstanceType<T>, 'id'>): InstanceType<T> { return {} as InstanceType<T>; } const instance = createModel(Child, {title: 'test', child :true }); // 类型检查通过
原写法出错原因
原代码中Model.create是一个泛型方法,Parameters<T['create']>[0]得到的是未绑定的Omit<T, 'id'>(这里的T是泛型参数,并非子类的实例类型),因此无法正确识别子类的额外属性。通过绑定this类型或直接指定InstanceType<T>,我们将参数类型与子类实例类型关联起来,解决了类型不匹配的问题。
内容的提问来源于stack exchange,提问作者Alexander Nenashev

