TypeScript: Extending vs anding vs oring

I felt like this could do with an explanation because for those who are just starting out with typescript, the difference between these three things may not be incredibly obvious. Hopefully I can explain in such a way that it’ll all make sense to you in just a few minutes.

Here are a couple of interfaces that we’ll use to demonstrate our extending etc.

interface IOne {
  one: 1;
}

interface ITwo {
  two: 2;
}

If we create another interface that extends IOne, this new interface will also contain the key one with value 1, but we can define other keys in there too.

interface IThree extends IOne {
  three: 3;
}

So this is equivalent to writing:

interface IThree {
  one: 1;
  three: 3;
}

When extending we cannot, however, override the value of the one key, because it is very explicit.

interface IThree extends IOne {
  one: 2; // Not happy
  three: 3;
}

If the value of one was something less specific like number, we could define a more specific type in the extending interface.

interface IOne {
  one: number;
}

interface IThree extends IOne {
  one: 1; // Happy
  three: 3;
}

Unfortunately, as far as I know, there is no way to extend an interface and remove keys from the parent interface. Extending is, exactly as it sounds, only for adding things to an existing interface.

Using the & operator for types is similar to extending, but simply combines both of the types.

type TThree = IOne & ITwo;

This type will contain both of the keys: one and two.

This is equivalent to writing:

interface IThree {
  one: 1;
  two: 2;
};

The | operator in types is best utilized when defining function parameters. It essentially means that an argument may be of any of the types in its definition. E.g.

type TOneOrTwo = IOne | ITwo;

function plusOne (input: TOneOrTwo) {

}

This function will only accept objects that match either the interface of IOne or ITwo.

plusOne({one: 1}); // Happy
plusOne({two: 2}); // Happy

plusOne({}); // Not happy
plusOne({one: 1, two: 2}); // Not happy

In order figure out which of the types the input is, you’d have to use type guards as you cannot access either the key one or two as they do not exist on both types. You can read a bit more about type guards in this post.