从函数重载签名中的对象值推断键类型

Infer key type from value of object in function overload signature

我定义了一个用于映射可迭代值的函数,与 Array.prototype.map() 非常相似,但我已将其简化为基本上等效于此示例,因为这些细节无关紧要:

function select <T, U> (array: T[], selectFn: (value: T, index: number, array: T[]) => U, thisArg?: unknown): U[] {
  return array.map(selectKeyOrFn, thisArg);
}

select(['foo', 'bar'], s => s.length);

到目前为止,还不错。一切编译。现在我想为第二个参数接受一个函数或一个 keyof T 。我不确定如何最好地为此重载签名定义类型参数,但这是我失败的方法:

type KeyOfValue<T, U extends T[keyof T]> = U extends T[infer K] ? K : never;

function select <T, U extends T[keyof T]> (array: T[], selectKey: KeyOfValue<T, U>): U[]
function select <T, U> (array: T[], selectFn: (value: T, index: number, array: T[]) => U, thisArg?: unknown): U[]
function select <T, U> (array: T[], selectKeyOrFn: (U extends T[keyof T] ? KeyOfValue<T, U> : never) | ((value: T, index: number, array: T[]) => U), thisArg?: unknown): U[] {
  return typeof selectKeyOrFn === 'function'
    ? array.map(selectKeyOrFn, thisArg)
    : array.map(value => value[selectKeyOrFn]);
}

select(['foo', 'bar'], 'length');
select(['foo', 'bar'], s => s.length);

主要问题似乎是我的 KeyOfValue 类型不允许我尝试使用 infer K 作为 T 的索引的语法:

Type 'K' cannot be used to index type 'T'.

我怎样才能成功地制作这个重载函数的原型?我不想求助于像 KeyOfValue 这样的辅助类型,但目前我想不出解决这个问题的方法。

我认为你是 over-complicating 这里有条件类型的东西。为键设置类型参数并使用类型查询会更好。同样对于实现签名,您可以合并两个重载签名,无需使用与 return 相同的 U 类型:

function select <T, K extends keyof T> (array: T[], selectKey: K): T[K][]
function select <T, U> (array: T[], selectFn: (value: T, index: number, array: T[]) => U, thisArg?: unknown): U[]
function select <T, U, K extends keyof T> (array: T[], selectKeyOrFn: K | ((value: T, index: number, array: T[]) => U), thisArg?: unknown): T[K][] | U[] {
  return typeof selectKeyOrFn === 'function'
    ? array.map(selectKeyOrFn, thisArg)
    : array.map(value => value[selectKeyOrFn]);
}

select(['foo', 'bar'], 'length');
select(['foo', 'bar'], s => s.length);