如何为数组键取嵌套对象值的reduce函数定义TypeScript类型?
问题描述
有如下嵌套对象:
// nested object const obj = { one: { two: { three: 'xxxx', }, four: { five: 5, }, six: [1, 2, 3], }, }
希望通过键数组从该嵌套对象中获取对应值,以下JavaScript代码可正常运行,但在TypeScript中无法通过类型校验:
const result = ['one', 'two', 'four'].reduce( (acc, key) => acc[key], obj, ) // undefined const result = ['one', 'two', 'three'].reduce( (acc, key) => acc[key], obj, ) // xxxx const result = ['one', 'six'].reduce( (acc, key) => acc[key], obj, ) // [1, 2, 3]
TSLint报错信息:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ one: { two: { three: string; }; four: { five: number; }; six: number[]; }; }'. No index signature with a parameter of type 'string' was found on type '{ one: { two: { three: string; }; four: { five: number; }; six: number[]; }; }'.ts(7053)
已知报错原因是键数组中的元素被推断为string类型,无法匹配对象的具体键类型,需要可行的类型定义方案。
解决办法
1. 用递归泛型实现强类型路径校验(推荐)
可以写一套类型工具,让TypeScript能识别嵌套对象的合法路径,同时自动推导返回值类型:
// 定义对象的合法嵌套路径类型 type Path<T> = T extends object ? { [K in keyof T]: [K] | [K, ...Path<T[K]>] }[keyof T] : []; // 实现取值函数 function getNestedValue<T, P extends Path<T>>(obj: T, path: P): P extends [infer K, ...infer Rest] ? Rest extends Path<T[K]> ? getNestedValue<T[K], Rest> : T[K] : T { return path.reduce((acc, key) => acc[key], obj) as any; } // 使用示例 const result1 = getNestedValue(obj, ['one', 'two', 'four']); // 类型自动推断为 undefined const result2 = getNestedValue(obj, ['one', 'two', 'three']); // 类型自动推断为 string const result3 = getNestedValue(obj, ['one', 'six']); // 类型自动推断为 number[]
这个方案会在编译阶段就拦截无效路径,同时保留完整的类型信息,最符合TypeScript的类型安全理念。
2. 简易类型断言快速消错
如果不需要严格的类型校验,只是想先消除报错,可以给acc加个类型断言:
const result = ['one', 'two', 'three'].reduce( (acc, key) => (acc as Record<string, any>)[key], obj, );
缺点是返回值会变成any,丢失类型推导,适合临时快速解决问题的场景。
3. 类型守卫做基础类型检查
想要兼顾一点类型安全又不想写复杂工具,可以用类型守卫缩小acc的类型范围:
const result = ['one', 'two', 'three'].reduce((acc, key) => { if (typeof acc === 'object' && acc !== null && key in acc) { return acc[key as keyof typeof acc]; } return undefined; }, obj);
这种方式下返回值会被推断为string | undefined,能避免完全的any,同时做基础的合法性检查。
内容的提问来源于stack exchange,提问作者Yutaka Fujii
相关产品推荐
相关产品推荐

