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

TypeScript无法从工厂实例推断元组实际类型的问题

问题描述

我编写了如下工厂类代码:

class ElementFinder {
  doSomething(): boolean {
    return true;
  }
}

interface Cell {
  getKey(): string;
}

interface CellFactory<T extends Cell[]> {
  createCellForIndex<I extends number>(index: I, elementFinder: ElementFinder): T[I]
}

class CellElementFactory<T extends Cell[]> implements CellFactory<T> {
  constructor(private cells: [...{ [I in keyof T]: new (elementFinder: ElementFinder) => T[I] }]) { }

  createCellForIndex<I extends number>(index: I, elementFinder: ElementFinder): T[I] {
    const Ctor = this.cells[index];
    return new Ctor(elementFinder);
  }
}

所有传入工厂构造函数数组的类都必须实现Cell接口,且构造函数签名兼容CellConstructor类型。

直接创建工厂实例时,返回值类型可被正确推断,示例代码如下:

class CellA implements Cell {
  constructor(elementFinder: ElementFinder) { }
  getKey(): string {
    return 'A'
  }
}

class CellB implements Cell {
  constructor(elementFinder: ElementFinder) { }
  getKey(): string {
    return 'B'
  }
}

class CellC implements Cell {
  constructor(element: ElementFinder) { }
  getKey(): string {
    return 'C'
  }
}


const cellFactory = new CellElementFactory([CellA, CellB, CellC, CellB, CellC, CellA]);
const cell1 = cellFactory.createCellForIndex(0, new ElementFinder()); // 类型为CellA
const cell2 = cellFactory.createCellForIndex(1, new ElementFinder()); // 类型为CellB
const cell3 = cellFactory.createCellForIndex(2, new ElementFinder()); // 类型为CellC
const cell4 = cellFactory.createCellForIndex(3, new ElementFinder()); // 类型为CellB
const cell5 = cellFactory.createCellForIndex(4, new ElementFinder()); // 类型为CellC
const cell6 = cellFactory.createCellForIndex(5, new ElementFinder()); // 类型为CellA

存在的问题

我编写了一个使用上述工厂实例的类,希望从传入的工厂实例中推断单元格类型元组,但始终无法实现预期效果,相关代码如下:

class CellRenderer<F extends CellFactory<any>> {
  constructor(
    private factory: F
  ) { }

  getCellByIndex<I extends number>(index: I, element: ElementFinder):
    (F extends CellFactory<infer T> ? T : never)[I] {
    return this.factory.createCellForIndex(index, element);
  }
}

调用该类方法时,TypeScript并未推断出索引对应的具体单元格类型,仅将返回值识别为基础Cell类型,精度不符合预期,示例代码如下:

const renderer = new CellRenderer(cellFactory);
const cell1R = renderer.getCellByIndex(1, new ElementFinder()); // 类型为Cell,精度不足
问题原因

原实现通过F extends CellFactory<infer T>推断元组类型T时,CellFactory<any>的泛型约束会触发TypeScript的类型宽化,将原本的固定顺序元组类型推断为普通Cell[]数组类型,丢失了索引和具体单元格类型的对应关系,最终取T[I]时只能得到所有单元格类型的联合父类Cell。

解决方案

不需要修改原有工厂接口和实现类,直接调整CellRenderer的类型推导逻辑:绕开对元组T的整体推断,直接从工厂实例的createCellForIndex方法签名中,匹配传入索引I对应的返回值类型即可。修改后的代码如下:

class CellRenderer<F extends Pick<CellFactory<Cell[]>, 'createCellForIndex'>> {
  constructor(
    private factory: F
  ) { }

  getCellByIndex<I extends number>(index: I, element: ElementFinder): 
    F['createCellForIndex'] extends (index: I, elementFinder: ElementFinder) => infer R ? R : never {
    return this.factory.createCellForIndex(index, element);
  }
}

修改后调用方法即可得到精确的类型推断:

const renderer = new CellRenderer(cellFactory);
const cell1R = renderer.getCellByIndex(1, new ElementFinder()); // 类型正确推断为CellB
const cell2R = renderer.getCellByIndex(2, new ElementFinder()); // 类型正确推断为CellC

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 23:36:37