如何为提取泛型对象指定键子集的函数强类型化返回值?
问题:实现基于参数键推断的强类型返回值函数
需求是实现一个函数,传入目标类型(如Foo)和该类型的键数组,返回值为对应键的Pick类型,且键从参数中自动推断,属性类型与原类型一致:
interface Foo { a: string b: string c: string } const value = myFunction<Foo>(['b', 'c']) // value 类型必须为 Pick<Foo,'b'|'c'>
尝试的错误方案及问题
方案A:仅指定目标类型泛型
interface User { name: string; age: number; address: string; } function functionA<T extends Record<string, any>>(keys: readonly Extract<keyof T, string>[]): {[K in typeof keys[number]: T[K]]} { // 实现忽略 } const u1 = functionA<User>(['address']) u1.name // 无错误提示,返回类型未正确限制
function functionAA<T extends Record<string, any>>(keys: readonly Extract<keyof T, string>[]): Pick<T,typeof keys[number]> { // 实现忽略 } const u2 = functionAA<User>(['address']) u2.name // 无错误提示,返回类型未正确限制
问题:typeof keys[number]会被推断为keyof T(TypeScript无法从传入的数组字面量中窄化类型),导致返回类型等价于T而非Pick<T, 指定键>。
方案B:双泛型参数
function functionB<T extends Record<string, any>, K extends Extract<keyof T, string>>(keys: readonly K[]): Pick<T,K> { // 实现忽略 } const u2 = functionB<User>(['address']) // 错误:"Expected 2 arguments, but got 1" const u3 = functionB(['address']) u3.address // 类型为 any
问题:必须显式传入两个泛型参数,仅传T会报错;不传泛型则T被推断为any,失去类型约束。
正确解决方案
通过泛型参数默认值和数组字面量的类型推断结合,让TypeScript自动推断键类型K,同时允许只指定目标类型T:
interface User { name: string; age: number; address: string; } function myFunction<T extends Record<string, any>, K extends keyof T = keyof T>(keys: readonly K[]): Pick<T, K> { // 示例实现(可根据需求替换) return {} as Pick<T, K>; } // 仅指定T,自动推断K为'address' const u1 = myFunction<User>(['address']) u1.address // string u1.name // 报错:属性'name'不存在于类型'Pick<User, "address">' // 自动推断T和K const u2 = myFunction(['name', 'age'] as const) u2.name // string u2.age // number u2.address // 报错:属性'address'不存在于类型'Pick<{ name: string; age: number; }, "name" | "age">'
原理说明
- 给
K设置默认值keyof T,当只传入T时,TypeScript会尝试从keys参数中推断更具体的K类型,而非直接使用默认值。 - 使用
readonly K[]确保数组字面量的类型被正确窄化(配合const断言可进一步提升推断精度)。 - 返回类型
Pick<T, K>严格限制为仅包含指定键的类型。
内容的提问来源于stack exchange,提问作者plfort
相关产品推荐
相关产品推荐

