如何基于元组首元素推导第二个元素的类型(含对象点标记)
实现严格类型安全的两层对象路径元组
需求是创建最多两层的类型安全对象路径数组,确保只有合法的路径组合被允许——比如拦截["weekend", "monday"]这种无效组合,同时支持单段路径(如["year"]),且路径长度超过2时直接报错。
先看原代码的问题:
interface Days { monday: string; tueday: string; wednesday: string; thursday: string; friday: string; saturday: string; sunday: string; } interface Weekend { saturday: string; sunday: string; } interface Example { days: Days; weekend: Weekend; year: string; } type KeysOfUnions<T> = T extends T ? keyof T : never; type ExamplePath<T extends keyof Example = keyof Example> = [T, KeysOfUnions<Example[T]>?]; const correctlyErrors: ExamplePath = ["days", "test"]; // 正确报错 const allowsCorrectPath: ExamplePath = ["days", "monday"]; // 正确 const allowsIncorrectPaths: ExamplePath = ["weekend", "monday"]; // 错误:允许了无效组合
原类型通过KeysOfUnions把所有嵌套类型的键合并成了一个大联合,导致不同父键对应的子键被混在一起,无法约束["weekend", "monday"]这种非法组合。同时直接对Example[T](联合类型)用keyof会因为包含基础类型(如string)而报错。
解决方案:基于父键动态推导子键的联合元组类型
我们可以用分布式条件类型和映射类型,让元组的第二个元素类型严格依赖于第一个元素的具体值:
// 通用类型:生成目标对象的所有合法两层/单层路径元组 type TwoLevelPath<T> = // 单段路径:所有顶层键,元组长度固定为1 | [keyof T] // 两段路径:仅当顶层键对应的值是对象时,生成「顶层键 + 子键」的元组 | { [K in keyof T]: T[K] extends object ? [K, keyof T[K]] : never }[keyof T]; // 针对Example接口生成路径类型 type ExamplePath = TwoLevelPath<Example>;
测试效果
const correctlyErrors: ExamplePath = ["days", "test"]; // ❌ 报错:"test"不属于Days的键 const allowsCorrectPath: ExamplePath = ["days", "monday"]; // ✅ 合法 const blocksInvalidPaths: ExamplePath = ["weekend", "monday"]; // ❌ 报错:"monday"不属于Weekend的键 const allowsSingleSegment: ExamplePath = ["year"]; // ✅ 合法 const rejectsLongerPaths: ExamplePath = ["days", "monday", "extra"]; // ❌ 报错:元组长度超过2
类型工作原理
- 单段路径支持:
[keyof T]直接生成所有顶层键的单元素元组,确保像["year"]这种无嵌套的路径合法。 - 两段路径约束:通过映射类型遍历每个顶层键
K,判断T[K]是否是对象类型(排除字符串、数字等基础类型):- 如果是对象,生成
[K, keyof T[K]]的元组,只允许该顶层键对应的合法子键; - 如果不是对象,返回
never(会被联合类型自动忽略)。
- 如果是对象,生成
- 自动过滤无效组合:最终的联合类型只包含所有合法的路径元组,不同顶层键对应的子键不会交叉混淆。
适配多层嵌套场景
即使你的实际接口有5层嵌套,这个类型依然适用——它只关注前两层的键,自动忽略更深的层级,完全符合需求。
内容的提问来源于stack exchange,提问作者Jarvis Prestidge
相关产品推荐
相关产品推荐

