Typescript - optional parameter validation not sufficient to determine type when passing it as a parameter to another function?

Viewed 281

This might seem like an odd question at first, but I'll try to explain it the best I can.

Let's take a look at this following (and non-realistic) example:

const foo = (x: number) => {
    return x * x;
}

const isXUndefined = (x?: number) => x === undefined;

const boo = (x?: number) => {
    if (isXUndefined(x)) {
        return 0;
    }

    return foo(x);
}

As expected, this code fails compiling since x can be both number and undefined. What I struggle to see here - is why invoking isXUndefined isn't enough for the type inferrer to assure that x is an actual number?

A working example on the other hand would be:

const foo = (x: number) => {
    return x * x;
}

const isXUndefined = (x?: number) => x === undefined;

const boo = (x?: number) => {
    if (x === undefined) {
        return 0;
    }

    return foo(x);
}

Why is this validation only working when making it "on the surface" but not from within another function?

Here's a playground with both examples

3 Answers

You can solve this by doing the following

const foo = (x: number) => {
    console.log("foo")
    return x * x;
}

const isXUndefined = (x?: number) => x === undefined;

const boo = (x?: number) => {
    if (isXUndefined(x)) {
        return 0;
    }

    return foo(x!);
}

By adding the !, you're telling the compiler that x won't be null or undefined here, and it won't complain.

JSFiddle: https://jsfiddle.net/nrd7hmkp/

Add x is undefined as return type for the function. See docs

const foo = (x: number) => {
    console.log("foo")
    return x * x;
}

const isXUndefined = (x?: number): x is undefined => x === undefined;

const boo = (x?: number) => {
    if (isXUndefined(x)) {
        return 0;
    }

    return foo(x);
}

I have a more difficult case where x is a member of an obj and the I pass obj into a validation function where I check x (and others validation if needed). Here's my code:

const foo = (obj: IOBJ) => {
    return obj.x * obj.y;
}

interface IOBJ {
    x?: number;
    y: number;
}

const isObjValid = (obj: IOBJ) => obj.x !== undefined;

const boo1 = (obj: IOBJ) => {
    if (!isObjValid(obj)) {
        return 0;
    }

    return foo(obj);
}

and here's my playground

Related