1

In typescript if you have two interfaces and as a union type, why doesn't it discriminate which type members you can declare?

interface IFish {
  swim: () => void;
}

interface ICat {
  meow: () => void;
}

type Pet = IFish | ICat;

const pet: Pet = {
  meow: () => {
    //
  },
  swim: () => {
    //
  }
};
jbockle
  • 633
  • 5
  • 11
  • i'm expecting typescript to allow meow OR swim not both – jbockle Sep 06 '18 at 18:54
  • Logically an OR means in english terms, P and/or Q. That is to say the only way that P|Q is false is if P and Q are both false. P and Q can both be true and there is no issue with that – richbai90 Sep 06 '18 at 18:58
  • See answer in [linked question](https://stackoverflow.com/questions/50346958/unexpected-behaviour-using-index-type-with-union-type-signature) and try `Xor` if you want something like exclusive unions. @MikhailBurshteyn's answer is also fine. – jcalz Sep 06 '18 at 19:08
  • 1
    @jcalz I would have gone with a more general answer for this one. Just for two types in a union is pretty restrictive. I think we can do better with something like this: `type Pet = { swim: () => void; } | {meow: () => void;}; type NoExcessPropertiesHelper = T extends any ? T & Record, never> : never; type NoExcessProperties = NoExcessPropertiesHelper const pet: NoExcessProperties = { meow: () => { }, swim: () => { } };` – Titian Cernicova-Dragomir Sep 06 '18 at 19:11

1 Answers1

2

Your interfaces define which members an object must have, but it doesn't define which members an object may not have. As a result, it is allowed for an IFish to have a swim method and vice versa.

Take a look at the updated example:

interface IFish {
    swim: () => void;
    meow: undefined;
}

interface ICat {
    meow: () => void;
    swim: undefined;
}

type Pet = IFish | ICat;

const pet: Pet = {  // compilation error here
    meow: () => {
        //
    },
    swim: () => {
        //
    }
};

Here it is explicitly stated that an IFish must not define meow, and an ICat must not define swim. However, the problem with this approach is that if you have more than 2 interfaces and methods, you'll have to add a lot of these undefineds to your type definitions.

Mikhail Burshteyn
  • 4,762
  • 14
  • 27