假设我想让一个函数更灵活,允许作为参数:
- 一个类型为number的元素
- 任何泛型元素+将其转换为数字的转换器函数
type ElementConvertor<T> = T extends number ? never : (element: T) => number
function isNumber(x: any): x is number {
return typeof x === "number"
}
function evaluate<T>(element: T, toNumber: ElementConvertor<T>) {
if (isNumber(element)) return element
else return toNumber(element)
}
const main = () => {
const array1 = [1, 2, 3];
const array1Value = array1.map(x => evaluate(x));
// -> TS compiler complains here
//input.tsx(7, 34): An argument for 'toNumber' was not provided.
const array2 = [{ value: 1 }, { value: 2 }, { value: 3 }]
const array2Value = array2.map((x => evaluate(x, ({ value }) => value)))
console.log(array1Value, array2Value)
}
TS游乐场链接
我错过了什么?感谢
您可以使用typescript重载签名。关于函数重载的文档
过载签名和实现:
//delcare overload signatures
function evaluate(element: number): number;
function evaluate<T>(element: T, toNumber: ElementConvertor<T>): number;
// implementation has to be compatibel with all overload signatures
function evaluate<T>(element: T, toNumber?: ElementConvertor<T>) {
if (isNumber(element)) return element
else {
if (toNumber !== undefined) {
return toNumber(element)
} else {
//even though it is not easy possible we have to handle it
throw new Error(`Cannot evaluate, the toNumber is not defiend, input is not a number. Input value: ${element}`)
}
}
}
记住,类型是不保证的,所以无论采用何种方法,您都可以使用以下代码轻松失败:
evaluate({} as number);
evaluate(undefined as any as number);
打字游戏场在这里
您可以使用以下条件类型内联您想要的行为:
function evaluate<T>(element: T, ...[toNumber]: T extends number ? [] : [toNumber: (element: T) => number]) {
if (typeof toNumber !== "undefined") return toNumber(element);
return element;
}
然而,这不像使用重载那样可读,但它确实有效。
游乐场