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

TypeScript:无需第二个泛型,能否获取数组参数的具体键联合类型?

问题描述

我想要创建一个泛型函数,该函数使用泛型类型描述对象,并接受该泛型类型的键组成的数组。函数返回另一个函数,后者接受该泛型类型的对象,并返回仅包含传入工厂函数的键的切片。

原始函数示例:

function createSlicer<T extends object>(keys: Array<keyof T>) {
  return (obj: T) => keys.reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {});
}

期望调用方式:

interface Person { name: string; age: number; gender: string; } 

// 期望类型:(obj: Person) => Pick<Person, 'name'>
const nameSlicer = createSlicer<Person>(['name']);
// 期望类型:(obj: Person) => Pick<Person, 'age'>
const ageSlicer = createSlicer<Person>(['age']); 
// 期望类型:(obj: Person) => Pick<Person, 'name' | 'gender'>
const nameAndGenderSlicer = createSlicer<Person>(['name', 'gender']); 

如果使用第二个泛型K来约束键的类型,可以定义正确的返回类型,但这样调用时要么不指定泛型,要么必须同时指定T和K,无法实现上述仅指定T的调用方式:

function createSlicer<T extends object, K extends keyof T>(keys: K[]): (obj: T) => Pick<T, K> {
  return (obj: T) => keys.reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {});
}

请问是否可以在不使用第二个泛型类型的情况下,获取传入keys的联合类型,从而为createSlicer定义正确的返回类型?


解决方案

无法完全避免使用第二个泛型参数来精确捕获keys的具体联合类型(因为TypeScript需要一个泛型来保存这个具体的键集合,而非宽泛的keyof T),但可以通过语法技巧实现**仅显式指定T,让编译器自动推断K**的调用体验,以下是两种可行方案:

方案一:使用柯里化(Curried)函数拆分泛型参数

将createSlicer拆分为两层函数:第一层接受显式指定的T,返回的第二层函数自动从传入的keys推断K的类型:

function createSlicer<T extends object>() {
  return <K extends keyof T>(keys: K[]) => {
    return (obj: T): Pick<T, K> => {
      // 断言返回类型为Pick<T, K>,避免TS推断为{}
      return keys.reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {} as Pick<T, K>);
    };
  };
}

调用方式(仅需指定T):

interface Person { name: string; age: number; gender: string; }

const nameSlicer = createSlicer<Person>()(['name']);
const ageSlicer = createSlicer<Person>()(['age']);
const nameAndGenderSlicer = createSlicer<Person>()(['name', 'gender']);

方案二:使用TypeScript 4.7+的部分类型参数推断

如果你的项目使用TypeScript 4.7或更高版本,可以使用通配符_让编译器自动推断K,仅显式指定T:

function createSlicer<T extends object, K extends keyof T>(keys: K[]): (obj: T) => Pick<T, K> {
  return (obj: T) => keys.reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {} as Pick<T, K>);
}

调用方式:

const nameSlicer = createSlicer<Person, _>(['name']);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:57:10