TypeScript:重载函数时如何将泛型类型转换为另一个泛型类型?
TypeScript: How to cast generic type to another generic type when overloading a function?
我想定义一个从数组创建字典的通用函数。该函数将 array
、keySelector
和可选的 valueSelector
作为参数。如果未提供 valueSelector
,则函数回退到身份函数。我希望 Typescript 能够理解类型 V
与 T
相同。相反,编译器给我错误 Type 'T' is not assignable to type 'V'
.
export function arrayToDictionary<T, K extends string | number | symbol, V>(
array: T[],
keySelector: (item: T) => K,
valueSelector: (item: T) => V = (item) => item // ERROR Type 'T' is not assignable to type 'V'
): Record<K, V> {
return array.reduce(
(acc, curr) => ({ ...acc, [keySelector(curr)]: valueSelector(curr) }),
{} as Record<K, V>
);
}
这里是 link 到 TypeScript Playground。
我找到的唯一解决方案是使用 any
关键字。
valueSelector: (item: T) => V = (item: any) => item
期望的结果如下:
const array = [
{ id: 1, name: "John" },
{ id: 2, name: "Will" },
{ id: 3, name: "Jane" },
];
// dict1 type should be Record<number, {id: number; name: string; }>
const dict1 = arrayToDictionary(array, p => p.id);
// dict2 type should be Record<number, string>
const dict2 = arrayToDictionary(array, p => p.id, p => p.name);
有没有更好的方法来定义函数的类型?
你可以把valueSelector
参数的return类型从简单的V
放宽到V | T
,这正是你想表达的,同时时间为您的泛型 V
提供默认类型(如果您不将第三个参数传递给该函数,它将被推断为 unknown
)。
function arrayToDictionary<T, K extends string | number | symbol, V = T>(
array: T[],
keySelector: (item: T) => K,
valueSelector: (item: T) => V | T = item => item
): Record<K, V> {
return array.reduce(
(acc, curr) => ({ ...acc, [keySelector(curr)]: valueSelector(curr) }),
{} as Record<K, V>
);
}
const arr = [1,2,3,4]
const dict1 = arrayToDictionary(arr, n => n.toString() + '!', n => n > 2) //Record<string, boolean>
const dict2 = arrayToDictionary(arr, n => n.toString() + '!') //Record<string, number>
我觉得你可以考虑用function overloading:
function arrayToDictionary<T, K extends string | number | symbol>(
array: T[],
keySelector: (item: T) => K
) : Record<K, K>;
function arrayToDictionary<T, K extends string | number | symbol, V>(
array: T[],
keySelector: (item: T) => K,
valueSelector: (item: T) => V
): Record<K, V>;
function arrayToDictionary<T, K extends string | number | symbol, V>(
array: T[],
keySelector: (item: T) => K,
valueSelector?: (item: T) => V
): Record<K, V> {
return array.reduce(
(acc, curr) => ({ ...acc, [keySelector(curr)]: valueSelector?.(curr)??curr }),
{} as Record<K, V>
);
}
虽然这不能直接解决问题,但它确实提供了关于函数签名的明确反馈。无论使用哪个签名来调用它,该实现只应涵盖函数的作用。
我想定义一个从数组创建字典的通用函数。该函数将 array
、keySelector
和可选的 valueSelector
作为参数。如果未提供 valueSelector
,则函数回退到身份函数。我希望 Typescript 能够理解类型 V
与 T
相同。相反,编译器给我错误 Type 'T' is not assignable to type 'V'
.
export function arrayToDictionary<T, K extends string | number | symbol, V>(
array: T[],
keySelector: (item: T) => K,
valueSelector: (item: T) => V = (item) => item // ERROR Type 'T' is not assignable to type 'V'
): Record<K, V> {
return array.reduce(
(acc, curr) => ({ ...acc, [keySelector(curr)]: valueSelector(curr) }),
{} as Record<K, V>
);
}
这里是 link 到 TypeScript Playground。
我找到的唯一解决方案是使用 any
关键字。
valueSelector: (item: T) => V = (item: any) => item
期望的结果如下:
const array = [
{ id: 1, name: "John" },
{ id: 2, name: "Will" },
{ id: 3, name: "Jane" },
];
// dict1 type should be Record<number, {id: number; name: string; }>
const dict1 = arrayToDictionary(array, p => p.id);
// dict2 type should be Record<number, string>
const dict2 = arrayToDictionary(array, p => p.id, p => p.name);
有没有更好的方法来定义函数的类型?
你可以把valueSelector
参数的return类型从简单的V
放宽到V | T
,这正是你想表达的,同时时间为您的泛型 V
提供默认类型(如果您不将第三个参数传递给该函数,它将被推断为 unknown
)。
function arrayToDictionary<T, K extends string | number | symbol, V = T>(
array: T[],
keySelector: (item: T) => K,
valueSelector: (item: T) => V | T = item => item
): Record<K, V> {
return array.reduce(
(acc, curr) => ({ ...acc, [keySelector(curr)]: valueSelector(curr) }),
{} as Record<K, V>
);
}
const arr = [1,2,3,4]
const dict1 = arrayToDictionary(arr, n => n.toString() + '!', n => n > 2) //Record<string, boolean>
const dict2 = arrayToDictionary(arr, n => n.toString() + '!') //Record<string, number>
我觉得你可以考虑用function overloading:
function arrayToDictionary<T, K extends string | number | symbol>(
array: T[],
keySelector: (item: T) => K
) : Record<K, K>;
function arrayToDictionary<T, K extends string | number | symbol, V>(
array: T[],
keySelector: (item: T) => K,
valueSelector: (item: T) => V
): Record<K, V>;
function arrayToDictionary<T, K extends string | number | symbol, V>(
array: T[],
keySelector: (item: T) => K,
valueSelector?: (item: T) => V
): Record<K, V> {
return array.reduce(
(acc, curr) => ({ ...acc, [keySelector(curr)]: valueSelector?.(curr)??curr }),
{} as Record<K, V>
);
}
虽然这不能直接解决问题,但它确实提供了关于函数签名的明确反馈。无论使用哪个签名来调用它,该实现只应涵盖函数的作用。