Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Silence typescript error "Property does not exist on type never"

Tags:

typescript

This question is related to this issue with the TypeScript compiler:

https://github.com/Microsoft/TypeScript/issues/11498

The discussion ranges far and wide over how to fix the compiler but I didn't see any work arounds.

The short description of the problem is that:

var bar: Foo | null | undefined = null;

results in the type of 'bar' being narrowed to "never". If you have an if block that sets the variable, then it widens, but a callback does not widen the type.

Here's a playground link if you want to see the error.

I found a workaround, but I don't like it (but it does work).

Is there a better work around?

class  Quz {
    foreach(func: ((x: number) => boolean)){
        for(var i = 0; i != 10;i++ ){
            var isDone = func(i);
            if(isDone) break;
        }
    }
}

interface Foo {
    getValue(): string;
}

var bar: Foo | null | undefined;

/* Is there a better way to silence the stupid error??? */
if(Math.random() < 2) {
    bar = null;
}


var quz = new Quz();

console.warn("Starting foreach")

quz.foreach(v => {
    if(v === 6){
        bar = {getValue() { return "z"}};
        return true;
    }
    return false;
})

var baz = (bar ? bar.getValue() : "?")

console.warn(baz);
like image 868
Reginald Blue Avatar asked Oct 16 '25 01:10

Reginald Blue


1 Answers

The control flow analysis TypeScript uses to narrow variables of union type, such as when you assign a value to it, doesn't properly account for what happens when these variables are changed in closures. This is the subject of microsoft/TypeScript#9998. It's not computationally feasible for the compiler to follow control flow into and out of functions, so it makes some approximations. In this case the relevant approximation is that calling functions will not have any effect on closed-over variables.

So bar is indeed reassigned inside the callback, but the compiler doesn't see it. And thus it is never widened from its initial assignment to null. Oops.


In cases like this, the "standard" workaround I've seen, as mentioned in this comment on ms/TS#9998, is to block the assignment narrowing by using a type assertion. So you change

var bar: Foo | null | undefined = null;

to

var bar = null as Foo | null | undefined;

This doesn't change the underlying type of the bar variable, but it does change the apparent type, since null as Foo | null | undefined is only seen by the compiler as type Foo | null | undefined. Thus bar is not narrowed by the assignment, and so it doesn't erroneously believe that bar must be nullish inside (bar ? bar.getValue() : "?").

It's not a perfect solution, since it turns off a useful feature, but at least it lets you make progress.

Playground link to code

like image 159
jcalz Avatar answered Oct 18 '25 19:10

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!