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

TypeScript使用infer与typeof实现工厂模式相关类型疑问咨询

问题1:infer与构造函数类型相关疑问

这个类型是TS中典型的「条件类型+类型推断」用法,作用是从构造函数类型中提取对应实例的类型,和TS内置的InstanceType工具类型逻辑完全一致,拆分解释各部分含义:

  • T extends new () => infer R 是条件判断逻辑:判断传入的泛型T是不是无参构造函数
    • new () => xxx是TS中构造函数的固定类型写法:new标记该类型可以被new关键字调用,括号内是构造参数的类型定义,箭头后面是new执行后返回的实例类型
    • infer R的作用是让TS自动捕获该构造函数返回的实例类型,存入类型变量R中。infer只能在条件类型的extends子句中使用,专门用于类型运算时动态提取未知的目标类型
  • 整个三元表达式逻辑:如果T是合法的无参构造函数,就返回它的实例类型R,否则返回never标记入参类型不合法

举个实际示例:

class Truck {}
// 传入的T是Truck类本身的类型(不是实例类型)
type TruckInstance = ExtractInstanceType<typeof Truck> 
// 最终TruckInstance的类型就是Truck,即new Truck()返回的实例类型

问题2:联合类型作为索引签名的疑问

TS的类型运算和JS运行时逻辑是完全独立的两套规则:JS中传单个键取单个值是运行时行为,而这里的typeof userMap[Keys]是类型层面的索引访问运算,规则如下:
当用联合类型作为索引访问对象类型时,TS会自动遍历联合类型的每一个成员,分别取对应的值类型,最终把所有结果拼成新的联合类型返回。

对应你给出的代码举示例:

// 假设userMap的运行时结构如下
const userMap = {
  dev: Developer,
  manager: Manager
}
// typeof userMap拿到该对象的类型:{ dev: typeof Developer, manager: typeof Manager }
// keyof 取该对象所有键的字面量联合类型:'dev' | 'manager'
type Keys = keyof typeof userMap;
// 用联合类型Keys做索引访问,等价于取每个键对应值类型的联合
// 即 typeof userMap['dev'] | typeof userMap['manager']
// 最终结果就是 typeof Developer | typeof Manager
type userTypes = typeof userMap[Keys];

这个特性非常常用,比如要提取某个对象所有值的类型联合,直接写typeof Obj[keyof typeof Obj]即可,无需手动枚举。


MyCloner类实现参考

符合你需求的可直接运行的实现如下,类型推断完全匹配预期:

interface IClonable {
  clone(): IClonable;
  color: string;
}

// 实现Truck类满足IClonable接口
interface TruckOptions {
  color: string;
  fuel: string;
}
class Truck implements IClonable {
  color: string;
  fuel: string;
  constructor(options: TruckOptions) {
    this.color = options.color;
    this.fuel = options.fuel;
  }
  clone(): Truck {
    return new Truck({ color: this.color, fuel: this.fuel });
  }
}

class MyCloner {
  // 泛型约束保证入参必须实现IClonable,返回值自动推断为对应实例的数组
  cloneWithRandomColors<T extends IClonable>(source: T, count: number): T[] {
    const result: T[] = [];
    const colorPool = ['red', 'blue', 'green', 'yellow', 'black', 'white'];
    for (let i = 0; i < count; i++) {
      const cloned = source.clone() as T;
      cloned.color = colorPool[Math.floor(Math.random() * colorPool.length)];
      result.push(cloned);
    }
    return result;
  }
}

// 调用方式和你要求的完全一致
const vehicleCloner = new MyCloner();
const truck = new Truck({color: 'red', fuel: 'electric'});
const myTenElectricTrucks = vehicleCloner.cloneWithRandomColors(truck, 10);
// 自动推断myTenElectricTrucks类型为Truck[]

内容的提问来源于stack exchange,提问作者JC1700

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 13:39:01