从函数重载签名中的对象值推断键类型
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);
我定义了一个用于映射可迭代值的函数,与 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);