TypeScript分布式条件类型问题:AnimalLabel类型赋值报错排查
问题原因及解决方案
问题根源
TypeScript采用结构类型系统,判断类型兼容性时仅依据结构是否一致,而非类的名称。你的Monkey、Pirate、Cow类结构完全相同(仅包含legCount: number属性),因此TypeScript认为这三个类的类型互相兼容。当条件类型T extends Monkey判断时,Pirate的结构符合Monkey的结构,导致第一个分支直接匹配,返回'Monkey label',而非预期的'Pirate label'。
解决方案
方案1:添加唯一标识属性(推荐)
给每个子类添加一个独一无二的字面量类型属性,让类的结构产生差异,从而让TypeScript能区分它们:
abstract class Animal { abstract readonly legCount: number; abstract readonly type: string; } class Monkey extends Animal { legCount: number = 2; type: 'monkey' = 'monkey'; } class Cow extends Animal { legCount: number = 4; type: 'cow' = 'cow'; } class Pirate extends Animal { legCount: number = 1; type: 'pirate' = 'pirate'; } export type AnimalLabel<T extends Animal> = T extends { type: 'monkey' } ? 'Monkey label' : T extends { type: 'pirate' } ? 'Pirate label' : T extends { type: 'cow' } ? 'Cow label' : never; const label: AnimalLabel<Pirate> = 'Pirate label'; // 编译通过
方案2:使用私有成员实现名义类型
TypeScript对包含私有成员的类会采用名义类型判断,只有同一个类的实例才会被认为兼容。我们可以给每个类添加一个私有标识成员:
abstract class Animal { abstract readonly legCount: number; private _brand: never; // 私有标识成员 } class Monkey extends Animal { legCount: number = 2; private _brand: never; } class Cow extends Animal { legCount: number = 4; private _brand: never; } class Pirate extends Animal { legCount: number = 1; private _brand: never; } export type AnimalLabel<T extends Animal> = T extends Monkey ? 'Monkey label' : T extends Pirate ? 'Pirate label' : T extends Cow ? 'Cow label' : never; const label: AnimalLabel<Pirate> = 'Pirate label'; // 编译通过
内容的提问来源于stack exchange,提问作者Andreas Backman
相关产品推荐
相关产品推荐

