You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中类型安全的柯里化pick()与pluck()实现难题

类型安全的柯里化 pick() 和 pluck() 实现

原代码存在的问题

  • pick() 返回 Partial<T>,这会将所有属性标记为可选,但实际上我们期望返回的对象包含所有指定属性(类型与原对象一致),因为传入的 obj 必然包含这些属性。
  • pluck() 的返回类型无法正确推断,TypeScript 无法通过 reduce 追踪嵌套属性访问的类型,导致返回值类型不准确。

修正后的实现

类型安全的 pick()

export const pick =
  <K extends readonly string[]>(...props: K) =>
  <T extends Record<K[number], unknown>>(obj: T): Pick<T, K[number]> => {
    return props.reduce((result, nextKey) => {
      result[nextKey] = obj[nextKey];
      return result;
    }, {} as Pick<T, K[number]>);
  };

说明:

  • 使用 Pick<T, K[number]> 作为返回类型,确保返回对象仅包含指定属性,且类型与原对象对应属性完全一致。
  • 通过 {} as Pick<T, K[number]> 初始化累加器,让 TypeScript 识别其正确类型。
  • 泛型约束 T extends Record<K[number], unknown> 确保传入对象包含所有指定属性。

类型安全的 pluck()

首先定义递归辅助类型处理嵌套属性的类型推导:

type Pluck<T, K extends readonly string[]> = 
  K extends [infer FirstKey, ...infer RestKeys]
    ? FirstKey extends keyof T
      ? RestKeys extends readonly string[]
        ? Pluck<T[FirstKey], RestKeys>
        : never
      : never
    : T;

然后实现 pluck() 函数:

export const pluck =
  <K extends readonly string[]>(...props: K) =>
  <T>(obj: T): Pluck<T, K> => {
    return props.reduce((current, key) => current[key], obj) as Pluck<T, K>;
  };

说明:

  • 辅助类型 Pluck<T, K> 递归遍历属性列表 K,逐步推导嵌套属性的最终类型。
  • 当属性列表为空时,直接返回原对象类型 T。
  • 使用 as Pluck<T, K> 帮助 TypeScript 识别 reduce 结果的正确类型(TypeScript 无法自动推导递归嵌套类型)。

使用示例

// 使用 pick()
const pickNameAndAge = pick('name', 'age');
const user = { name: 'Alice', age: 30, email: 'alice@example.com' };
const picked = pickNameAndAge(user);
// picked 类型为 { name: string; age: number }

// 使用 pluck()
const pluckAddressCity = pluck('address', 'city');
const userWithAddress = { name: 'Bob', address: { city: 'New York', zip: '10001' } };
const city = pluckAddressCity(userWithAddress);
// city 类型为 string

内容的提问来源于stack exchange,提问作者Adam B.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 10:50:39