Home > database >  TS complains that argument of type "never" is not provided
TS complains that argument of type "never" is not provided

Time:10-29

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)
}

Link to TS playground

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);

Typescript playground here

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.

Playground

  • Related