如何为TypeScript自定义pick/reject工具函数添加泛型类型?
实现类型安全的pick和reject函数(带自动类型推导)
这是个非常实用的需求——不用依赖Ramda这类库,自己实现的工具函数也能享受到TypeScript的类型推导能力。我们可以通过泛型约束结合TypeScript内置的工具类型,完美实现你想要的效果:
1. pick 函数实现
这个函数会从对象中挑选指定的键,返回只包含这些键的新对象,同时TypeScript能自动推导返回值的类型:
function pick<T extends object, K extends keyof T>(keys: K[], object: T): Pick<T, K> { const result = Object.assign( {}, ...keys.map(k => (k in object ? { [k]: object[k] } : {})) ); // 因为Object.assign的返回类型无法自动匹配Pick<T,K>,这里做安全的类型断言 return result as Pick<T, K>; }
泛型说明:
T extends object:约束传入的第二个参数必须是对象类型K extends keyof T:确保传入的keys数组中的每个元素都是原对象T的有效键,避免传入不存在的键- 返回类型
Pick<T, K>:TypeScript内置工具类型,专门用来从类型T中提取K指定的键值对集合
2. reject 函数实现
这个函数会排除对象中指定的键,返回剩下的键值对,同样支持自动类型推导:
function reject<T extends object, K extends keyof T>(keys: K[], object: T): Omit<T, K> { const result = Object.assign( {}, ...Object.keys(object) // Object.keys返回的是string[],这里断言为K是安全的,因为我们只处理T的键 .filter(k => !keys.includes(k as K)) .map(k => ({ [k]: object[k] })) ); // 同样做安全的类型断言,匹配Omit<T,K>类型 return result as Omit<T, K>; }
泛型说明:
- 泛型参数
T和K的约束和pick函数一致 - 返回类型
Omit<T, K>:TypeScript内置工具类型,用来从类型T中移除K指定的键值对
3. 使用示例(符合你的预期)
按照你的测试代码,这样使用就能得到精准的类型推导:
type Item = {title: string, description: string} const test: { [key: string]: Item } = { a: { title: 'TitleA', description: 'DescriptionA' }, b: { title: 'TitleB', description: 'DescriptionB' }, c: { title: 'TitleC', description: 'DescriptionC' }, d: { title: 'TitleD', description: 'DescriptionD' } }; // 注意:如果需要更精确的字面量键类型,给数组加as const断言 const test1 = pick(['a', 'c'] as const, test); // 类型推导结果:{ a: Item; c: Item } const test2 = reject(['a', 'c'] as const, test); // 类型推导结果:{ b: Item; d: Item }
为什么要加as const?
默认情况下,TypeScript会把['a', 'c']这类数组字面量推导为string[],导致泛型K被推断为string(而不是"a" | "c")。加上as const后,数组会被视为只读的字面量数组,TypeScript就能精确推断出K是"a" | "c",进而让返回类型更精准。
额外优势
- 类型安全:如果传入的keys包含原对象不存在的键,TypeScript会直接报错
- 零依赖:完全基于原生TypeScript和JavaScript实现,不需要引入任何第三方库
- 自动推导:无需手动指定泛型参数,TypeScript会根据传入的参数自动推导返回值类型
内容的提问来源于stack exchange,提问作者juliusbangert




