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
相关产品推荐
相关产品推荐

