Search code examples
typescriptcompiler-errorsundefined

TypeScript gives compile error "Object is possibly 'undefined'." but only with class variable and not scope variables


In TypeScript with strict enabled (TypeScript playground):

type Foo = { a: number; b: number; }

class Bar {

    #a?: Foo;

    bar() {
        const a = this.#a?.a;
        if (!a) {return;}

        // Error: Object is possibly 'undefined'.
        const b = this.#a.b;
    }

    bar2() {
        const obj = this.#getFoo();

        const a = obj?.a;
        if (!a) {return;}

        // No error:
        const b = obj.b;       
        console.log(b);
    }

    #getFoo() : Foo | undefined {
        return undefined;
    }

}

Why does TS understand correctly in bar2() that obj.b cannot be undefined; but not this.#a.b in bar()? Is there a logic case I am missing?

In bar(), if I assign const tmp = this.#a it compiles successfully as well.


UPDATE: apparently it has something to do with the mutability of a variable. If I use let obj = this.#getFoo(), the error shows up for obj as well.


Solution

  • The ts compiler cannot guarantee that the this.#a value will not change between the two property accesses because TypeScript assumes that the value of a class property can change at any time, even between two consecutive lines of code i.e. TypeScript cannot be sure that the value of this.#a remains the same between the two lines.

    In the bar2() method, the value of obj is assigned to the result of this.#getFoo(), which is a local variable. TypeScript can guarantee that the value of obj will not change between the two property accesses, so it does not complain.

    In your last Update, when you use let obj = this.#getFoo(), TypeScript assumes that the value of obj can potentially change between the two property accesses since it is mutable. As a result, the error shows up for obj as well.

    Now, to fix the issue you can assign this.#a to a local constant variable. Something like:

    bar() {
            const obj = this.#a; // <----Notice this
            const a = obj?.a;
            if (!a) {return;}
    
            // Error: Object is possibly 'undefined'.
            const b = obj.b;
        }
    

    TYPESCRIPT PLAYGROUND

    Context References