TypeScript递归类型与可选属性兼容问题求助
解决TypeScript中带可选属性对象的StatePath类型失效问题
原类型定义
以下是用于通过字符串/索引数组导航对象或嵌套数组的TypeScript类型:
export type PredicateFunction<ArrayType> = (array: ArrayType, index?: number) => boolean; export type IndexOrPredicateFunction<Type> = number | PredicateFunction<Type>; export type StatePathKey = IndexOrPredicateFunction<any> | string; export type StatePath<Obj, Path extends (string | IndexOrPredicateFunction<any>)[] = []> = object extends Obj ? Path : Obj extends object ? (Path | // 检查对象是否为数组 (Obj extends readonly any[] ? // 数组仅允许索引或断言函数 StatePath<Obj[number], [...Path, IndexOrPredicateFunction<Obj[number]>]> // 对象则生成所有可能键的类型 : { [Key in string & keyof Obj]: StatePath<Object[Key], [...Path, Key]> }[string & keyof Obj])) : Path;
正常工作场景
当使用不含可选属性的接口State1时,该类型可以正常推导路径:
interface State1 { test: { nestedTest: boolean } } const t1: StatePath<State1> = ['test', 'nestedTest']; // 类型检查通过
问题场景
当接口包含可选属性(如State2)时,该类型无法正确推导可选属性的路径,赋值操作会报错:
interface State2 { test: { nestedTest?: boolean } } // 无法将 ['test', 'nestedTest'] 赋值给 StatePath<State2>
解决方案
修正后的类型定义如下,解决了可选属性的推导问题:
export type PredicateFunction<ArrayType> = (array: ArrayType, index?: number) => boolean; export type IndexOrPredicateFunction<Type> = number | PredicateFunction<Type>; export type StatePathKey = IndexOrPredicateFunction<any> | string; export type StatePath<Obj, Path extends (string | IndexOrPredicateFunction<any>)[] = []> = object extends Obj ? Path : Obj extends null | undefined // 处理null/undefined分支,避免推导中断 ? Path : Obj extends object ? (Path | // 数组类型处理逻辑不变 (Obj extends readonly any[] ? StatePath<Obj[number], [...Path, IndexOrPredicateFunction<Obj[number]>]> // 修复笔误+处理可选属性:取属性的非undefined类型进行推导 : { [Key in string & keyof Obj]: StatePath<NonNullable<Obj[Key]>, [...Path, Key]> }[string & keyof Obj])) : Path;
修正说明
- 修复核心笔误:将
Object[Key]改为Obj[Key],避免错误引用全局Object类型。 - 添加
null/undefined分支处理:当属性值为null或undefined时终止路径推导,避免类型崩溃。 - 可选属性处理:使用
NonNullable<Obj[Key]>提取可选属性的实际类型,忽略undefined分支,确保路径可以深入到可选属性内部。
验证修正效果
修正后,带可选属性的接口可以正常推导路径:
interface State2 { test: { nestedTest?: boolean } } const t2: StatePath<State2> = ['test', 'nestedTest']; // 类型检查通过
内容的提问来源于stack exchange,提问作者Stefan Holzapfel
相关产品推荐
相关产品推荐

