如何在TypeScript中深度遍历对象并断言其类型?
可行实现方案:基于属性路径的类型断言工具
我们可以通过通用路径解析+类型守卫的组合来实现你要的功能,既消除嵌套断言,又保留TypeScript的类型安全。下面是具体实现步骤:
1. 定义路径解析的工具类型
先实现两个核心类型,让TypeScript能自动推导属性路径对应的目标类型,确保路径合法性:
// 提取对象的所有可嵌套属性路径 type Path<T> = T extends object ? { [K in keyof T]: K extends string | number ? `${K}` | `${K}.${Path<T[K]>}` : never }[keyof T] : never; // 根据路径获取对应的目标类型 type PathValue<T, P extends Path<T>> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? Rest extends Path<T[K]> ? PathValue<T[K], Rest> : never : never : P extends keyof T ? T[P] : never;
2. 实现运行时路径检查的通用函数
写一个底层函数负责逐层解析路径,再封装断言逻辑:
function getPathValue<T, P extends Path<T>>(obj: T, path: P): PathValue<T, P> | undefined { return path.split('.').reduce((current, key) => { if (current == null || typeof current !== 'object') return undefined; return (current as Record<string, unknown>)[key]; }, obj as unknown) as PathValue<T, P> | undefined; } function assertPath<T, P extends Path<T>>( obj: T, path: P, validator?: (value: PathValue<T, P>) => boolean ): asserts obj is T & { [K in P]: NonNullable<PathValue<T, P>> } { const value = getPathValue(obj, path); if (value == null || (validator && !validator(value))) { throw new Error(`路径 ${path} 不存在或不符合规则`); } }
3. 封装你的assertUUID专属函数
基于通用逻辑,封装UUID格式的校验断言:
// UUID格式校验正则 const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; function assertUUID<T, P extends Path<T>>( obj: T, path: P ): asserts obj is T & { [K in P]: string } { assertPath(obj, path, (value) => { return typeof value === 'string' && UUID_REGEX.test(value); }); }
这个函数会一次性完成:路径存在性检查、非空校验、UUID格式验证,断言失败直接抛出错误。
4. 实际用法对比
原来的冗余写法:
// 嵌套条件+类型断言 if (effect?.language?.effect?.id?.effect) { const uuid = effect.language.effect.id.effect as string; // 还要额外写UUID格式校验 }
新的简化写法:
// 一行完成所有校验 assertUUID(effect, 'language.effect.id.effect'); // 断言后TypeScript自动识别该属性为合法UUID字符串 const uuid = effect.language.effect.id.effect;
额外优势
因为有Path<T>类型的支持,调用assertUUID时TypeScript会自动提示合法的属性路径,避免拼写错误,大幅提升开发效率。
内容的提问来源于stack exchange,提问作者Lance Pollard
相关产品推荐
相关产品推荐

