Why doesn't throw an error this union based type?

Viewed 54

I have in my TypeScript project a situation which could be simplified with the following:

Consider the following type Type:

type Type = {
  a: number;
} | {
  a: number;
  b: number;
} | {
  a: number;
  b: number;
  c: number;
};

I can define the t constant based on Type type:

const t: Type = {
  a: 1,
  c: 3
};

And it doesn't give me any error! Due the Type type definition, I couldn't define an object with a and c properties. But I can. Why?

Moreover, if I access c property:

console.log(t.c);

It gives me a transpitation error saying:

Property 'c' does not exist on type 'Type'. Property 'c' does not exist on type '{ a: number; }'.

I really don't know what's going on!

1 Answers

Typescript uses structural typing.

  1. Problem: Excess property check : The quirk of excess property checks is that for unions, it allows any property from any union constituent to be present in the assigned object literal.

const t: Type = { a: 1, c: 3 }; is valid because it conforms to the first part of you union { a: number; }.

This won't happen if the types don't match: for exemple :

type Type = {
    a: number;
    b: number
} | {
    a: string;
    b: string;
    c: number;
};

let t: Type = {  // KO 
    a: 1,
    c: 3
};

More on that topic : Excess Property check

2., console.log(t.c);, TS will warn you that the key c does not exist on every part of your union, hence the error message you see.

You can fix this with narrowing, an particularly the in operator :

if('c' in t) {
   console.log(t.c) // OK
}

Playground

Related