如何实现TypeScript中构造函数到泛型值类型的类型推断?
TypeScript类型推断问题:让resolve方法返回正确的资源类型
问题场景
以下代码中,调用resolve(MyResource)后,result的类型被推断为unknown,无法正确识别为{ foo: string }:
export abstract class EcsResourceFactory<TResult> { protected abstract build(): TResult } class ResourceContainer { public resolve< TResult, TFactory extends EcsResourceFactory<TResult>, >(type: new () => TFactory): TResult // Other overloads here public resolve(key: any): any { // implementation here } } class MyResource extends EcsResourceFactory<{ foo: string }> { protected build() { return { foo: 'bar' } } } const result = new ResourceContainer().resolve(MyResource) console.log(result.foo) // 此处result类型为unknown,无法访问foo属性
尝试修改泛型参数后仍未解决问题,需要让result的类型正确推断为{ foo: string },且实际场景中EcsResourceFactory的子类存在构造函数参数。
解决方案
核心问题是原泛型定义中,TypeScript无法自动从TFactory反向推导TResult。我们可以通过条件类型的类型推断或者调整泛型参数的定义方式,让TypeScript从传入的工厂类构造函数中提取出对应的TResult类型。
方法一:使用条件类型提取TResult
修改resolve方法的重载定义,仅保留TFactory泛型参数,然后通过infer关键字从EcsResourceFactory的继承关系中推断出TResult:
class ResourceContainer { // 调整后的重载:从TFactory中推断TResult,支持任意构造函数参数 public resolve<TFactory extends EcsResourceFactory<any>>( type: new (...args: any[]) => TFactory ): TFactory extends EcsResourceFactory<infer TResult> ? TResult : never public resolve(key: any): any { // implementation here } }
方法二:直接绑定TResult与工厂构造函数
如果不需要单独引用TFactory,也可以直接将泛型参数设为TResult,并要求传入的构造函数返回EcsResourceFactory<TResult>的实例:
class ResourceContainer { public resolve<TResult>( type: new (...args: any[]) => EcsResourceFactory<TResult> ): TResult public resolve(key: any): any { // implementation here } }
效果验证
修改后,调用resolve(MyResource)时,TypeScript会自动推断出TResult为{ foo: string },result的类型将正确识别,访问result.foo不会再有类型错误。
原理说明
- 方法一中,
infer TResult告诉TypeScript:如果TFactory是EcsResourceFactory<X>的子类,就把X赋值给TResult,从而实现类型提取。 - 方法二中,直接将构造函数的返回类型与
EcsResourceFactory<TResult>绑定,TypeScript可以从传入的MyResource构造函数中,提取出它继承的EcsResourceFactory的泛型参数,也就是{ foo: string }。 - 两种方式都使用
new (...args: any[]) => ...来支持任意构造函数参数,适配实际场景中工厂类有构造参数的情况。
内容的提问来源于stack exchange,提问作者Charles HETIER
相关产品推荐
相关产品推荐

