TypeScript如何定义带visible属性节点的对象点分路径联合类型?
问题:为DOM树可见性对象的点分路径参数定义TypeScript类型
场景与需求
有一个表示DOM树可见性的对象:
const visibilities = { food: { visible: true, fruit: { visible: true, apple: { visible: false } }, snack: {visible: false} } }
需要实现工具函数getVisibilities,通过点分路径字符串获取指定节点的可见性,调用方式如下:
getVisibilities(visibilities, 'food.fruit.apple')
核心需求是为函数的第二个参数(点分路径字符串)定义TypeScript类型,确保只能传入合法的节点路径。
尝试的方案及问题
尝试编写了如下递归类型定义:
type VisibilityString<Prop extends {[key: string]: any}> = {[K in keyof Prop]: Prop[K] extends {visible: boolean} ? K extends string ? K | `${K}.${VisibilityArray<Prop[K]>}`: never : never}[keyof Prop]
但出现错误:TypeError: VisibilityArray<Prop[K]> is not a string。将参数改为数组形式后,在TypeScript 4.4.4版本中仍存在类型推导错误。
解决方案
1. 点分路径字符串类型实现
通过递归类型筛选合法节点并拼接路径:
// 筛选对象中所有值包含visible属性的键 type VisibilityNodeKeys<T> = { [K in keyof T]: T[K] extends { visible: boolean } ? K : never; }[keyof T]; // 递归生成合法的点分路径字符串 type VisibilityPath<T> = VisibilityNodeKeys<T> extends infer K ? K extends string ? T[K] extends { visible: boolean } ? K | `${K}.${VisibilityPath<T[K]>}` : never : never : never; // 工具函数定义 function getVisibilities<T extends Record<string, any>>( obj: T, path: VisibilityPath<T> ): boolean { const keys = path.split('.'); let current: any = obj; for (const key of keys) { current = current[key]; if (!current) return false; // 路径不存在时返回默认值,可按需调整 } return current.visible; }
类型工作原理:
VisibilityNodeKeys先过滤出对象中所有值包含visible属性的键,排除无效节点VisibilityPath递归拼接路径:对于每个合法键,既可以单独作为路径,也可以拼接该键与子节点的合法路径- TypeScript会自动推导所有合法路径,比如
'food'、'food.fruit'、'food.fruit.apple'、'food.snack',非法路径会直接触发类型报错。
2. 数组形式路径的兼容实现(适配TS 4.4.4)
如果需要使用数组形式的路径,可采用以下兼容TS 4.4.4的类型定义:
// 筛选合法节点键 type VisibilityNodeKeys<T> = { [K in keyof T]: T[K] extends { visible: boolean } ? K : never; }[keyof T]; // 递归生成数组形式的合法路径 type VisibilityPathArray<T> = VisibilityNodeKeys<T> extends infer K ? K extends string ? T[K] extends { visible: boolean } ? [K] | [K, ...VisibilityPathArray<T[K]>] : never : never : never; // 数组形式的工具函数 function getVisibilitiesArray<T extends Record<string, any>>( obj: T, path: VisibilityPathArray<T> ): boolean { let current: any = obj; for (const key of path) { current = current[key]; if (!current) return false; } return current.visible; }
该写法通过元组递归拼接路径,避免了早期TS版本中字符串模板递归的兼容性问题。
内容的提问来源于stack exchange,提问作者Clifford B. Wolfe
相关产品推荐
相关产品推荐

