Say I want to make a function more flexible, by allowing as parameters either:
- an element of type number
- any generic element a converter function that converts it into a 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)
}
What am I missing? Thanks
CodePudding user response:
You can use typescript overload signatures. docs on Function Overloads
Overloading signatures and implementation:
//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}`)
}
}
}
Have in mind types are not guaranteed so that you can easy fail with the following code regardless of the approach:
evaluate({} as number);
evaluate(undefined as any as number);
CodePudding user response:
You're able to inline the behaviour you want with a conditional type like this:
function evaluate<T>(element: T, ...[toNumber]: T extends number ? [] : [toNumber: (element: T) => number]) {
if (typeof toNumber !== "undefined") return toNumber(element);
return element;
}
However, this isn't as readable as using overloads, but it does work.