sixtyfootersdude
sixtyfootersdude

Reputation: 27221

Property does not exist when using union type in typescript

Suppose that I have two interfaces:

interface Box {
    x: number
    y: number
}

and

interface ColouredBox {
    x: number
    y: number
    colour: string
}

Assume for the purpose of this question, that I cannot change the interfaces.

Now when constructing objects, I have code like this:

let a: Box|ColouredBox = {x: 1, y: 2}
if( something ){
    a.colour = "Blue"  // Compilation Error
}

I get this error on a.colour = "Blue":

Error:(24, 26) TS2339: Property 'colour' does not exist on type 'Box'.

Why? Is this a limitation of the TS compiler? Other than completely reconstructing the object, is there a different workflow that I could use?

Upvotes: 3

Views: 8934

Answers (2)

Jason
Jason

Reputation: 2820

Using a Partial

Instead of using a union type, you can try using a Partial

let a: Partial<ColouredBox> = {x: 1, y: 2}

The partial will set all the properties of the ColoredBox as optional.

Live example here.

Update:

Eliminate an Interface

If you only want the color part of the interface to be optional, you can eliminate one of the interfaces.

interface Box {
  x: number;
  y: number;
  color?: string;
}

Then to determine if you're dealing with a colored box:

if (box.color) { ... }

Type Assertion

If you stick to using both interfaces, you can specifically treat a as a ColouredBox using the as keyword (a.k.a. a Type Assertion).

let a: Box | ColouredBox = { x: 1, y: 2 };
(a as ColouredBox).colour = "Blue";

Upvotes: 2

bela53
bela53

Reputation: 3485

You can use the in type guard:

if ("colour" in a) {
    a.colour = "Blue"  // works
}

This will narrow to the union part ColouredBox based on the existence of property colour. In general, you can only select all common properties x/y of Box | ColouredBox, when not narrowed before.

Live code sample here

Upvotes: 9

Related Questions