为何TypeScript中`T extends B`无法在后续引用中限制T类型?
TypeScript泛型类型窄化失效的场景探究
PickedAttributeSimpler是一个带参数的泛型类型,其参数通过extends限制为极小集合——示例中仅'Moon'一种可能,但限制范围不一定这么窄。
原本预期,后续引用该泛型类型时,泛型参数会自动窄化为extends右侧指定的类型,特定场景下甚至能更窄。但在这个示例及多个简化后的复杂场景中,只有把泛型参数直接替换为extends右侧的类型,TypeScript才能正确推断出整个泛型的类型,符合预期行为。
但实际场景中,有时需要保留泛型参数:当类型实例指定的参数比限制类型更具体时,必须持续引用泛型参数来保持一致性——因此直接弃用泛型参数并非通用解决方案。
我想搞清楚,在哪些条件下,泛型参数的类型限制无法让后续引用自动体现窄化效果。
另外,有独立问题讨论过为何本示例中的部分简化方案(比如硬编码ObjectModelMap,或是在ObjectModel中使用条件类型)无法复现当前问题。
这可能是TypeScript的bug导致的,我找到最接近的issue是#33014,但它主要聚焦于函数而非其他类型定义。或许是我对问题的理解不到位,导致找不到正确的关键词搜索,也没法提交准确的报告(如果合适且不重复,欢迎提交新的bug报告)。
示例代码
import BN from "bn.js"; // 外部固定模型的简化版本;为简化示例省略了很多字段 type FieldNameWithValueMap = { Moon: [fieldName: "visibleStarsCount", fieldValue: BN] | [fieldName: "hostStar", fieldValue: string]; Planet: [fieldName: "visibleStarsCount", fieldValue: BN] | [fieldName: "hostPlanet", fieldValue: string]; } // type PlanetaryBodyName = keyof FieldNameWithValueMap; // 'Moon' | 'Planet'; 简化示例中未使用 type TupleToObject<T extends [string, any]> = { [key in T[0]]: Extract<T, [key, any]>[1] }; type ObjectModel<T extends keyof FieldNameWithValueMap> = TupleToObject<FieldNameWithValueMap[T]>; // 鼠标悬停查看这两个示例,类型符合预期;它们需要从导入的类型派生 type MoonObject2 = ObjectModel<'Moon'>; // {visibleStarsCount: BN; hostPlanet: string;} type PlanetObject2 = ObjectModel<'Planet'>; // {visibleStarsCount: BN; hostStar: string;} type DRYObjectModelMap = { [PlanetaryBodyClass in keyof FieldNameWithValueMap]: ObjectModel<PlanetaryBodyClass> } type PickedAttributeSimpler<T extends 'Moon'> = DRYObjectModelMap[T]['visibleStarsCount']; // 使用下面的定义而非上面的定义时,错误会消失 // type PickedAttributeSimpler<T extends 'Moon'> = DRYObjectModelMap['Moon']['visibleStarsCount']; function genericFunction< T extends 'Moon', // 简化示例中省略了包含 | 'Planet' 的联合类型 // 省略泛型参数并在下方所有用法中硬编码可以修复错误, // 但示例之外的实际函数版本需要保持泛型 >( PlanetaryBodyClass: T, ) : PickedAttributeSimpler<T> { // 直接用 DRYObjectModelMap[T]['visibleStarsCount'] 也会报错 return new BN(42); // 错误 TS(2322): 类型 'BN' 不能赋值给类型 'PickedAttributeSimpler<T>' } type ExamplePickedAttribute = PickedAttributeSimpler<'Moon'>; // 类型为 'BN',符合预期 type ExampleGenericPickedAttribute<T extends 'Moon'> = PickedAttributeSimpler<T>; // 类型为 DRYObjectModelMap[T]['visibleStarsCount'] type ExampleGenericInstanceOfPickedAttribute = ExampleGenericPickedAttribute<'Moon'>; // 类型为 BN,符合预期 type HoverToSeeHowTypeShouldResolve<T extends 'Moon'> = DRYObjectModelMap[T]['visibleStarsCount']; type HoverToSeeHowTypeShouldResolveInstance = HoverToSeeHowTypeShouldResolve<'Moon'>; // 类型为 BN,符合预期
内容的提问来源于stack exchange,提问作者WBT
相关产品推荐
相关产品推荐

