TypeScript报错:'T'可被约束'PrimaryCell'的不同子类型实例化
理解TypeScript条件类型引发的类型赋值错误
问题背景
我正在尝试理解这个TypeScript错误,使用了条件类型特性,可能对其工作原理存在误解。
代码示例
type PrimaryCell = "primary" | "header"; type BorderCell = "top" | "bottom"; type AnyCell = PrimaryCell | BorderCell; type Cell<T extends AnyCell, U extends string | string[]> = { content: U; type: T; }; type TallCell<T extends PrimaryCell> = Cell<T, string[]>; type ShortCell<T extends AnyCell> = Cell<T, string>; // 根据单元格类型确定允许的单元格数组类型 type TallOrShort<T extends AnyCell> = T extends PrimaryCell ? TallCell<T>[] | ShortCell<T>[] : ShortCell<T>[]; class RegularRow<T extends PrimaryCell, U extends TallOrShort<T>> { cells: U; type: T; constructor(cells: U) { this.cells = cells; this.type = this.cells[0].type; // <-- 报错位置 } }
报错信息
Type '"primary" | "header"' is not assignable to type 'T'. '"primary" | "header"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'PrimaryCell'. Type '"primary"' is not assignable to type 'T'. '"primary"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'PrimaryCell'.ts(2322)
临时解决方案(不够理想)
this.type = this.cells[0].type as T;
目标需求
通过TypeScript约束RegularRow类的单元格数组只能是Tall或Short类型(不能同时存在),并且单元格类型"primary"、"header"、"top"或"bottom"应决定单元格是否可以是Tall或Short类型,这就是TallOrShort条件类型的作用。
问题原因
TallOrShort<T>返回的是联合类型数组(TallCell<T>[] | ShortCell<T>[]),但TypeScript无法从联合数组中推断出this.cells[0].type的具体类型严格等于泛型T。因为泛型T可以是PrimaryCell的子类型(比如单独的"primary"),而this.cells[0].type的类型是PrimaryCell联合类型,TypeScript无法保证两者完全一致,所以抛出类型不匹配错误。
优化方案
方案1:简化泛型参数,强化类型关联
将RegularRow的泛型改为仅保留T,直接用TallOrShort<T>作为cells的类型,减少泛型复杂度的同时让类型推断更顺畅:
class RegularRow<T extends PrimaryCell> { cells: TallOrShort<T>; type: T; constructor(cells: TallOrShort<T>) { this.cells = cells; this.type = cells[0].type as T; } }
方案2:使用工具类型提取数组元素类型
定义工具类型ArrayElement提取数组的元素类型,辅助TypeScript识别元素类型与泛型T的关联:
type ArrayElement<Arr> = Arr extends Array<infer Elem> ? Elem : never; class RegularRow<T extends PrimaryCell, U extends TallOrShort<T>> { cells: U; type: T; constructor(cells: U) { this.cells = cells; this.type = (cells[0] as ArrayElement<U>).type as T; } }
方案3:重构类型结构,避免联合数组歧义
通过调整构造函数参数的结构,直接传入包含type的对象,完全消除类型推断的不确定性:
type RowCells<T extends PrimaryCell> = | { type: T; cells: TallCell<T>[] } | { type: T; cells: ShortCell<T>[] }; class RegularRow<T extends PrimaryCell> { cells: TallCell<T>[] | ShortCell<T>[]; type: T; constructor(row: RowCells<T>) { this.cells = row.cells; this.type = row.type; } }
内容的提问来源于stack exchange,提问作者Rio
相关产品推荐
相关产品推荐

