You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 15:27:46