1

If I create union types of shapes, it usually works as expected:

type BookT = { 
    id: string;
    title: string;
    price: number;
};
type MagazineT = { id: string, abstract: string };

type ReadingMaterialT = BookT | MagazineT;

const readingMaterial: ReadingMaterialT[] = [
    // ok: TS complains about missing abstract
    {
        id: '1',
    },
    // ok: TS complains about missing title
    {
        id: '2',
        price: 9,
    },
    // ok: TS complains about missing price
    {
        id: '3',
        title: 'foo',
    }
];

If, however, one shape of the union is a subtype of the other (here MagazineT is a subtype of BookT), the union doesn't work as expected anymore. In fact, in the array readingMaterial, TypeScript allows to add shapes which are neither of type BookT nor of type MagazineT:

type BookT = { 
    id: string;
    title: string;
    price: number;
};
type MagazineT = { id: string };

type ReadingMaterialT = BookT | MagazineT;

const readingMaterial: ReadingMaterialT[] = [
    // ok
    {
        id: '1'
    },
    // ? Should complain about missing title!
    {
        id: '2',
        price: 9,
    },
    // ? Should complain about missing price!
    {
        id: '3',
        title: 'foo',
    }
];

Why is the TypeScript compiler basically removing type safety in the second scenario? Or is it just luck that my first scenario works, and it is not recommended to build unions like that?

Note: I know that I could have added a literal type field to each shape (e.g. type: 'book' and type: 'magazine') in order to make this a discriminated union (which probably is the recommended thing to do here). I'm however asking this question because this behavior of the TS compiler really confuses me!

TypeScript Playground of my question

Andru
  • 5,954
  • 3
  • 39
  • 56

0 Answers0