TypeScript实现带正确类型推导的对象子集提取函数,支持嵌套键
原实现错误原因
- 没有声明第二个泛型捕获传入keys的具体字面量类型,TS只会把keys推导为
(keyof T)[],丢失了实际传入的键信息 - 返回值类型
Record<keyof typeof keys, any>逻辑错误:typeof keys是数组类型,keyof 数组拿到的是数组实例的属性(length、索引签名等),和期望提取的对象键完全无关
平级键提取实现(满足基础需求)
直接用TS内置的Pick工具类型即可实现正确的类型推导:
function extractFromObj<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> { return keys.reduce((newObj, curr) => { newObj[curr] = obj[curr]; return newObj; }, {} as Pick<T, K>); }
使用效果和要求的示例完全一致,解构时只会提示你传入的键,类型完全匹配原对象对应键的类型:
// 示例调用 const post = { id: 1, properties: {}, created_time: '', otherKey: '' }; const { id, properties, created_time } = extractFromObj(post, ['id', 'properties', 'created_time']); // 此时上面三个变量的类型会自动匹配post中对应键的类型,不会出现多余的otherKey提示
嵌套键提取实现(满足补充需求)
需要先定义两个TS工具类型,用于推导合法的路径和路径对应值的类型,再实现路径取值逻辑:
// 工具类型:推导对象所有合法的点分隔路径 type PathImpl<T, Key extends keyof T> = Key extends string ? T[Key] extends Record<string, any> ? | `${Key}.${PathImpl<T[Key], keyof T[Key]>}` | Key : Key : never; type Path<T> = PathImpl<T, keyof T>; // 工具类型:根据点分隔路径推导对应值的类型 type PathValue<T, P extends string> = P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? PathValue<T[Key], Rest> : never : P extends keyof T ? T[P] : never; // 根据路径从对象取值的工具函数 function getValueByPath(obj: any, path: string) { return path.split('.').reduce((curr, key) => curr?.[key], obj); } // 嵌套提取版本函数 function extractFromObjNested<T, P extends Path<T>>(obj: T, paths: P[]): { [K in P]: PathValue<T, K> } { return paths.reduce((newObj, path) => { newObj[path] = getValueByPath(obj, path as string); return newObj; }, {} as { [K in P]: PathValue<T, K> }); }
使用示例:
const obj = { "object": "page", "id": "b03f9945-528a-4a1c-a280-6972bbc49462", "created_time": "2021-11-07T11:24:00.000Z", "last_edited_time": "2021-11-07T11:24:00.000Z", "cover": null, "foo": { "bar": { "baz": { "properties": "hello world" } } } } const result = extractFromObjNested(obj, ['id', 'foo.bar.baz.properties']); // result的类型会自动推导为: // { // id: string; // 'foo.bar.baz.properties': string; // } // 解构时类型完全正确 const { id, 'foo.bar.baz.properties': nestedProp } = result;
如果需要提取后保留嵌套结构而非用路径做键,只需要调整reduce中的赋值逻辑即可,类型推导部分可以复用上面的Path和PathValue工具。
内容的提问来源于stack exchange,提问作者nas.engineer
相关产品推荐
相关产品推荐

