TypeScript类型谓词追加类型而非替换,如何让引擎识别类型继承?
解决TypeScript泛型交叉类型问题:让
has()方法正确扩展已知组件类型 问题核心
你当前的Test类用数组类型作为泛型参数表示已知组件,调用has()方法后,TypeScript会将原类型与新类型交叉(Test<[AComp, BComp]> & Test<[AComp, BComp, CComp]>),而非识别新类型是原类型的超集,导致get()方法返回值仍可能被推断为undefined。
解决方案:用联合类型替代数组类型表示已知组件
数组类型是有序结构,TypeScript无法自动识别[AComp, BComp, CComp]包含[AComp, BComp];而联合类型是无序的集合,TypeScript能直接识别AComp | BComp | CComp是AComp | BComp的超集,交叉后会自动合并为更宽的类型。
调整后的完整代码
abstract class Component { } class AComp extends Component { aValue = 1; } class BComp extends Component { bValue = 3; } type ClassOf<C extends Component> = new (...args: any[]) => C; // 基于联合类型判断返回值:如果C属于已知联合类型K,返回C,否则返回C | undefined type TestGetResult<C extends Component, K extends Component> = C extends K ? C : (C | undefined); // 泛型参数改为联合类型K,默认never表示初始无已知组件 class Test<K extends Component = never> { components = new Map<string, Component>(); constructor(components: Component[]) { components.forEach(comp => this.components.set(comp.constructor.name, comp)); } // has方法返回类型改为this is Test<K | C>,扩展已知组件的联合类型 has<C extends Component>(comp: ClassOf<C>): this is Test<K | C> { return [...this.components.keys()].includes(comp.name); } get<C extends Component>(comp: ClassOf<C>): TestGetResult<C, K> { return this.components.get(comp.name) as TestGetResult<C, K>; } } class SomeStore { list: Test<any>[] = []; add(test: Test<any>) { this.list.push(test); } // 返回类型改为Test<C>,表示找到的实例已知包含C组件 getTestWithSomeComponent<C extends Component>(comp: ClassOf<C>): Test<C> { for (let test of this.list) { if (test.has(comp)) { return test; } } throw new Error('No test found'); } } /////////////////////////////////////////////////////////////////////////////////////// const store = new SomeStore(); const test1 = new Test<AComp | BComp>([new AComp(), new BComp()]) store.add(test1); const testAfterGet = store.getTestWithSomeComponent(AComp); if (testAfterGet.has(BComp)) { const a = testAfterGet.get(AComp); const b = testAfterGet.get(BComp) // 此时b的类型为BComp,无undefined console.log(a.aValue); console.log(b.bValue); // 不再报错 }
关键调整点说明
- 泛型参数从数组改为联合类型:将
Test<K extends Component[]>改为Test<K extends Component = never>,用联合类型表示已知组件的集合,而非有序数组。 - 修改
has()方法的类型断言:把this is Test<[...K, C]>改为this is Test<K | C>,让TypeScript知道检查后已知组件集合扩展了C类型。 - 适配
TestGetResult类型:基于联合类型判断,而非数组元素的K[number],简化逻辑同时提升类型推断准确性。
调整后,调用has(BComp)后testAfterGet的类型会变为Test<AComp | BComp>,get(BComp)会直接返回BComp,不会出现undefined的推断问题。
内容的提问来源于stack exchange,提问作者GolfwangBananas
相关产品推荐
相关产品推荐

