typescripttypinginference

Type inference with union types in TypeScript


I have a type C which is the union of A and B types. I have a variable of type C and after assigning a B type it seems that the type infer is not working when B and C have one property with the same name.

class A {
    prop1: string;
}

class B {
    prop1: string;
    prop2: string;
}

type C = A | B;

class test {
    thisIsC: C;

    constructor() {
        this.thisIsC = this.getTypeB(); //This doesn't make thisIsC inferred to B
        const b = this.getTypeB(); //This works fine
        const a = this.thisIsC;
    }

    getTypeB(): B {
        return new B();
    }
}

If the properties don't collide, the problem doesn't appear and the type infer is done properly (for example):

class A {
    prop1: string;
}

class B {
    prop2: string;
    prop3: string;
}

type C = A | B;

class test {
    thisIsC: C;

    constructor() {
        this.thisIsC = this.getTypeB(); //This makes thisIsC be inferred to B type
        const a = this.thisIsC;
    }

    getTypeB(): B {
        return new B();
    }
}

My question is if I can do something to improve this type inference (with colliding parameters) just to tell typescript that the variable "thisIsC" is of type B after the getTypeB() call. Apparently "If we have a value that has a union type, we can only access members that are common to all types in the union." which makes sense to me, but in my code, "getTypeB()" returns explicitly a type B. I just can solve the problem creating an aux variable or doing a manual type assertion with a type guard but I want to know if I can do anything to modify how this internally works.

Playground with the error: https://t.ly/iFZ0

Thanks in advance


Solution

  • I want to know if I can do anything to modify how this internally works.

    I would recommend tagging your A and B types.

    class A {
        prop1: string;
        tag: "A";
    
        constructor() {
            this.prop1 = "helloworld";
            this.tag = "A"
        }
    }
    
    class B {
        prop1: string;
        prop2: string;
        tag: "B";
    
        constructor() {
            this.prop1 = "helloworld";
            this.prop2 = "prop2";
            this.tag = "B";
        }
    }
    
    type C = A | B;
    
    class test {
        thisIsC: C;
    
        constructor() {
            this.thisIsC = this.getTypeB();
            const b = this.thisIsC;
    
            b.prop2;
        }
    
        getTypeB(): B {
            return new B();
        }
    }
    

    After tagging them typescript is able to properly infer the types.

    play