0

I have a union of two different types, and an array of this union type.
I want to get an error when I try to pass a combination that doesn't exist on any of the interfaces.

interface IMenuItem {
  title: string;
  theme: 'primary' | 'secondary';
}

interface IMenuDivider {
  isDivider: boolean;
  margin?: number;
}

type TItem = IMenuItem | IMenuDivider;

const items: TItem[] = [
  {
    title: 'item title',
    isDivider: true // doesn't error
  }
]

I'd expect this example to trigger an error since this combination isn't defined in any of the interfaces composing the union.

How should I type my TItem to achieve this?

Thanks!

Uri Klar
  • 3,800
  • 3
  • 37
  • 75
  • This doesn't seem to be reproducible. – jcalz Jul 30 '22 at 20:03
  • I updated the post. It was throwing an error because I forgot to mark `margin` as optional. But now that I did, it's not throwing an error – Uri Klar Jul 31 '22 at 07:55
  • 1
    This is a duplicate of https://stackoverflow.com/questions/46370222/why-does-a-b-allow-a-combination-of-both-and-how-can-i-prevent-it – jcalz Jul 31 '22 at 12:48

1 Answers1

0

Ok I figured it out. Apparently for this to work they need to share a property to discriminate by.
Not sure 100% why, but adding the type attribute works:

interface IMenuItem {
  type: 'item';
  title: string;
  theme: 'primary' | 'secondary';
}

interface IMenuDivider {
  type: 'divider';
  isDivider: boolean;
  margin?: number;
}

type TItem = IMenuItem | IMenuDivider;

const items: TItem[] = [
  {
    type: 'item',
    title: 'item title',
    isDivider: true // Error: Object literal may only specify known properties, and 'isDivider' does not exist in type 'IMenuItem'.
  }
]

PS - I can guess why. Without the type attribute, typescript can't really know which interface I was aiming for, so it can't tell me which property is wrong. ‍♂️

Uri Klar
  • 3,800
  • 3
  • 37
  • 75