TypeScript中如何声明valueMap函数返回类型以校验非法访问key
valueMap 类型精准优化方案
核心目标是在类型层面收集所有配置项的属性值作为合法访问键集合,访问非集合内的键时TypeScript会直接抛出类型错误,同时保留原有运行时逻辑不变。
优化后完整实现
// 递归提取传入数组所有配置项的属性值,组成合法访问键的联合类型 type ExtractValidKeys<T extends readonly any[]> = T extends readonly [infer FirstItem, ...infer Rest] ? FirstItem[keyof FirstItem] | ExtractValidKeys<Rest> : never; // 构建最终返回值的映射类型,建立合法键到对应配置项的类型关联 type ValueMapRes<T extends readonly any[]> = { [K in ExtractValidKeys<T>]: T extends readonly (infer Item)[] ? K extends Item[keyof Item] ? Item : never : never; }; const valueMap = <const T extends readonly Record<string, any>[]>(arr: T) => { return arr.reduce((ret: Record<string | number, T[number]>, item) => { Object.keys(item).forEach(key => { ret[item[key]] = item; }); return ret; }, {}) as ValueMapRes<T>; };
注:以上实现依赖TypeScript 5.0+ 新增的泛型const修饰符特性,低版本可移除泛型前的const关键字,调用时给传入的数组手动添加as const断言即可。
使用效果验证
const TASKS = valueMap([ { id: -1, key: 'all', name: 'all-task' }, { id: 1, key: 'editing', name: 'editing-task' }, { id: 2, key: 'done', name: 'done-task' }, ]); // 合法访问可正常推导对应配置项的精准类型,无报错 TASKS[-1] // 类型: { readonly id: -1; readonly key: "all"; readonly name: "all-task" } TASKS['all'] // 类型同上 TASKS['all-task'] // 类型同上 // 访问不存在的键时TS直接抛出类型错误 TASKS['not-exist'] // 报错信息:类型“ValueMapRes<[...]>”上不存在属性“not-exist”
补充说明
- 运行时逻辑和原实现完全一致,无额外性能损耗,仅补充了类型层的精准校验能力
- 如果需要兼容“访问不存在的键返回undefined”的动态场景,可将返回类型声明为
Partial<ValueMapRes<T>>,但该模式下不会触发非法键的类型报错 - 如果配置项存在重复的属性值,类型层面会自动把对应键映射到联合类型,和运行时后面的项覆盖前面项的逻辑保持一致
内容的提问来源于stack exchange,提问作者qinyuanbin
相关产品推荐
相关产品推荐

