如何在TypeScript中泛型定义剔除部分键的Record类型?
类型安全的Record过滤函数优化:避免重复指定保留键
我有一个用于过滤Record中部分键的函数,希望通过类型安全机制避免访问已被过滤的键。
现有实现
目前的类型定义如下:
// 过滤OnlySelected结果中的never类型键 type OmitNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K] } // 仅保留类型V中包含的键 type OnlySelected<T extends object, V> = { [K in keyof T]-?: K extends V ? T[K] : never }
该类型在简单场景中表现良好:
type A = { a: number; b: string; c: string; d: string; e: string; } const a: A = { a: 1, b: "2", c: "3", d: "4", e: "5", } type MyType = OmitNever<OnlySelected<A, "a" | "b">>; // 类型MyType = { // a: number; // b: string; //}
痛点:泛型函数中重复指定键
但在泛型函数中使用这些类型时,必须手动将想要保留的键转换为联合类型,重复输入键才能保证类型安全,十分繁琐:
const filterRecord = <T extends Record<any, any>, TO_KEEP>(record: T, keys: Array<keyof T>) => { return Object.keys(record) .reduce((acc, it) => { if (keys.includes(it)) { acc[it as keyof OmitNever<OnlySelected<T, TO_KEEP>>] = record[it]; } return acc; }, {} as OmitNever<OnlySelected<T, TO_KEEP>>) } const res = filterRecord<A, "d" | "e">(a, ["d", "e"]); console.log(res.d) // 正常 console.log(res.e) // 正常 console.log(res.a) // 类型检查应报错,但需手动指定泛型才生效
优化方案
可以利用TypeScript内置的Pick类型简化实现,同时让TypeScript自动推导保留的键类型,避免重复输入:
优化后的函数
const filterRecord = <T extends object, TO_KEEP extends keyof T>(record: T, keys: TO_KEEP[]) => { const filtered = {} as Pick<T, TO_KEEP>; for (const key of keys) { filtered[key] = record[key]; } return filtered; };
使用方式
调用时无需手动指定泛型参数,TypeScript会自动从传入的keys数组推导TO_KEEP的类型:
const res = filterRecord(a, ["d", "e"]); console.log(res.d); // 类型正确,可正常访问 console.log(res.e); // 类型正确,可正常访问 console.log(res.a); // 类型检查报错:属性'a'不存在于类型'Pick<A, "d" | "e">'上
方案优势
- 自动类型推导:无需手动指定泛型参数,TypeScript会根据传入的键数组自动推导保留的键类型,避免重复输入。
- 内置类型简化代码:使用TypeScript内置的
Pick<T, K>类型替代自定义的OmitNever和OnlySelected,代码更简洁易读。 - 严格类型安全:返回值类型精确对应保留的键,访问已过滤的键会直接触发类型检查报错,保证类型安全。
内容的提问来源于stack exchange,提问作者Ioannis Noukakis
相关产品推荐
相关产品推荐

