In the previous post, I said that when you ask a waiter “Is [some dish] vegan?” you would not want to hear the answer “I don’t know”.

But what if you ask about a dish that they do not serve? Imagine asking “Are Powidltatschkerln vegan?” somewhere outside Austria. I would totally expect to hear “I don’t know” as an answer. (They are not, by the way).

So, we might need a boolean with a third “I don’t know” option, after all…

Union Types

At the end of the previous part of this series, I turned on “strict” type checking. That setting removes null and undefined from the possible values for all types (and it does more).

I would argue that this is the better default behavior for new projects. Without strict null checks enabled, null and undefined are part of every set and one cannot remove them. When we turn on strict, null and undefined are removed from every set—but we can bring them back:

//use directly:
const vegan : boolean | null = null

//...or define a type alias
type NullableBoolean = boolean | null
const glutenFree : NullableBoolean = null

The | here looks like the “logical or” operator of JavaScript—and you should also read it as “or”. So, vegan is a variable that is either a boolean or null and by defining the type alias NullableBoolean we get a type that is a boolean or the value null.

Intersection Types

What if we use & instead of |? Can we do that too?

type NullableBoolean = boolean | null
const n : NullableBoolean = null

type MaybeDefinedBoolean = boolean | undefined
const m : MaybeDefinedBoolean = undefined

type Intersection = NullableBoolean & MaybeDefinedBoolean
const u1 : Intersection = true
const u2 : Intersection = false
const u3 : Intersection = null //ERROR
//Type 'null' is not assignable to type 'boolean'.(2322)
const u4 : Intersection = undefined //ERROR
//Type 'undefined' is not assignable to type 'boolean'.(2322)

Here we get a new type Intersection that is both a NullableBoolean and a MaybeDefinedBoolean.

Every value of that type must satisfy all restrictions of NullableBoolean and all restrictions of MaybeDefinedBoolean. And there are only two values that are both

  • boolean | null and also
  • boolean | undefined

at the same time: Only the values true and false satisfy both restrictions.

Back to Sets

When I first learned about union and intersection types (in another type system, a few years ago), I could not remember why “and” is and “intersection” while “or” is a “union”. The word “and” always sounded like it would add something, so when I thought of “and”, I also thought of “union”. Which is wrong, of course.

For me, it became easier to distinguish the names when I learned to think about the sets of possible values.

When a variable can hold either values from one type or values from another type, the set of possible values for this variable is the union of the other two sets.

Our NullableBoolean can hold values from boolean or from null, so the set of possible values is the union of boolean and null.

Sets drawn for the two union types NullableBoolean and MaybeDefinedBoolean

When, on the other hand, a value for a variable must satisfy all requirements from two or more types, the set of possible values for this variable must come from the intersection of the other sets.

Our type Intersection must both be a NullableBoolean and a MaybeDefinedBoolean at the same time, and the only two values in this intersection of the two sets are the “original” boolean values true and false.

Set colored for the Intersection of NullableBoolean and MaybeDefinedBoolean

Conclusion

Union types (A | B) allow us to define types where the possible values are of either one type or another type. The possible values come from the union of the original sets. One simple thing we can do with unions is add more possible values (like null or undefined) to an existing type.

Intersection types (A & B) allow us to combine the restrictions of different other types. The possible values come from the intersection of the original sets. One simple thing we can do with them is remove possible values from an existing type.

I will get back to unions and intersections again in later posts—because one can not only use them toadd null or undefined to a type in strict mode, but do more interesting stuff with them.