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

TypeScript中约束pick函数选值后keys参数的可选属性

解决TypeScript Pick工具函数的重复key与智能提示问题

问题分析

你实现的pick函数能正确推导返回类型,但存在两个痛点:

  • 允许传入重复的key,TypeScript不会报错
  • 输入数组参数时,智能提示始终显示所有key,无法自动排除已选择的项

下面分两种场景给出解决方案:


方案1:数组参数版(解决重复key问题)

通过定义Unique辅助类型,约束keys数组的元素必须唯一,同时保留原函数的数组参数调用方式:

// 辅助类型:确保元组元素无重复
type Unique<T extends readonly any[]> = T extends [infer First, ...infer Rest]
  ? First extends Rest[number]
    ? never // 存在重复元素,返回never触发类型报错
    : [First, ...Unique<Rest>]
  : T;

const pick = <T extends object, K extends readonly (keyof T)[]>(
  obj: T,
  // 约束keys必须是无重复的元组,否则类型报错
  keys: K extends Unique<K> ? K : never
): Pick<T, K[number]> => {
  if (!obj) return {} as Pick<T, K[number]>;
  return keys.reduce((acc, key) => {
    return { ...acc, [key]: obj[key] };
  }, {} as Pick<T, K[number]>);
};

// 测试用例
const user = { name: 'Alice', age: 25, email: 'alice@example.com' };
pick(user, ['name', 'age']); // ✅ 正常
pick(user, ['name', 'age', 'age']); // ❌ TypeScript报错:类型不匹配

原理

  • Unique类型通过递归遍历元组,检查每个元素是否在剩余元素中存在,若存在则返回never,触发类型错误
  • 函数泛型约束K extends Unique<K>确保只有无重复的keys数组才能通过类型检查

方案2:柯里化版(解决重复key+智能提示问题)

如果需要实现「输入一个key后,智能提示剩余未选key」的效果,必须用柯里化方式,让每次选择key后,返回的函数自动排除已选的key:

// 辅助类型:计算剩余可选的key
type RemainingKeys<T extends object, Selected extends keyof T = never> = Exclude<keyof T, Selected>;

const pick = <T extends object>(obj: T) => {
  // 递归构造选择器
  const createPicker = <Selected extends keyof T>(selected: Selected[]) => {
    type AvailableKeys = RemainingKeys<T, Selected[number]>;
    
    return {
      // 选择下一个key,返回新的选择器
      pick: <K extends AvailableKeys>(key: K) => {
        return createPicker([...selected, key] as const);
      },
      // 完成选择,返回最终的Pick结果
      done: () => {
        return selected.reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {} as Pick<T, Selected[number]>);
      }
    };
  };

  return createPicker([]);
};

// 测试用例
const user = { name: 'Alice', age: 25, email: 'alice@example.com' };
// 调用方式:链式选择,每次pick后智能提示剩余未选key
const pickedUser = pick(user).pick('name').pick('age').done();
// pickedUser类型为 { name: string; age: number }
// 若重复选择已选key,TypeScript会报错:类型不在AvailableKeys中

原理

  • 柯里化函数每次接收一个key后,通过RemainingKeys类型排除已选的key,更新下一次可选的key集合
  • 递归调用createPicker,每次传递已选的key数组,确保后续的pick方法只能提示剩余未选的key
  • 最终调用done方法,执行实际的属性提取逻辑

内容的提问来源于stack exchange,提问作者MailHunter99

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 01:55:18