Union vs Intersection Mental Model

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

Professional TypeScript Masterclass Lessons

1.Introduction
free
⏱️ 1:54
2.Setup
free
⏱️ 5:44
3.Primitive Types
free
⏱️ 1:42
4.Instance Types
free
⏱️ 1:52
5.Arrays And Tuples
free
⏱️ 1:38
6.Objects
free
⏱️ 1:33
7.const declarations
free
⏱️ 1:03
8.Function Types
free
⏱️ 1:57
9.Structural Typing
free
⏱️ 2:10
10.Classes in TypeScript
free
⏱️ 1:48
11.Target Compiler Option
free
⏱️ 2:37
12.Generics
⏱️ 3:02
13.Special Types any And unknown
⏱️ 2:00
14.JavaScript to TypeScript
⏱️ 1:32
15.Frontend Projects
⏱️ 3:49
16.Type Assertions
⏱️ 2:15
17.Type Casting
⏱️ 1:16
18.Modules
⏱️ 1:55
19.Type Declarations
⏱️ 4:25
20.Creating NPM packages
⏱️ 3:20
21.Async Await
⏱️ 3:05
22.Running in NodeJS
⏱️ 1:40
23.Lexical this
⏱️ 2:34
24.readonly Modifier
⏱️ 1:59
25.Union Types
⏱️ 2:57
26.Literal Types
⏱️ 2:58
27.Type Narrowing
⏱️ 4:19
28.Discriminated Unions
⏱️ 3:29
29.Class Parameter Properties
⏱️ 1:02
30.Strict Compiler Option
⏱️ 6:18
31.null vs undefined
⏱️ 4:19
32.Intersection Types
⏱️ 2:03
33.Optional Modifier
⏱️ 2:47
34.Non Null Assertion Operator
⏱️ 3:40
35.Interfaces
⏱️ 2:28
36.Interface Declaration Merging
⏱️ 1:01
37.Types vs Interfaces
⏱️ 2:16
38.never Type
⏱️ 3:00
39.implements Keyword
⏱️ 1:25
40.Definite Assignment Assertion
⏱️ 2:31
41.User Defined Type Guards
⏱️ 2:02
42.Assertion Functions
⏱️ 3:42
43.Function Overloading
⏱️ 4:15
44.Call Signatures
⏱️ 2:53
45.Abstract Classes
⏱️ 1:53
46.Index Signatures
⏱️ 3:08
47.Readonly Arrays and Tuples
⏱️ 2:58
48.Double Assertions
⏱️ 2:20
49.const Assertions
⏱️ 3:55
50.this Parameter
⏱️ 2:33
51.Generic Constraints
⏱️ 2:43
52.typeof Type Operator
⏱️ 2:12
53.Lookup Types
⏱️ 3:12
54.keyof Type Operator
⏱️ 3:55
55.Conditional Types
⏱️ 4:39
56.Contitional Types with Unions and never
⏱️ 3:32
57.infer Keyword and `ReturnType<T>`
⏱️ 3:47
58.Mapped Types
⏱️ 2:48
59.Mapped Type Modifiers
⏱️ 3:37
60.Template Literal Type
⏱️ 4:28
61.Partial<T>
⏱️ 1:27
62.Required<T>
⏱️ 1:36
63.Readonly<T>
⏱️ 1:34
64.Record<K, T>
⏱️ 4:05
65.Project References
⏱️ 4:18
66.undefined vs. optional
⏱️ 2:48
67.satisfies Operator
⏱️ 2:42
68.PropertyKey Type
⏱️ 0:57
69.ThisType<T>
⏱️ 4:11
70.Awaited<T>
⏱️ 4:12
71.String Manipulation Types
⏱️ 3:36
72.Mapped Types as Clauses
⏱️ 4:01
73.Union vs Intersection Mental Model
⏱️ 3:36
74.Enums are Bad
⏱️ 8:11

Union vs Intersection Mental Model

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

Example Code

export type Name = { name: string };
export type Age = { age: number };

type Union = Name | Age | (Name & Age);

let union: Union;
union = { name: 'Jane' };
union = { age: 29 }
union = { name: 'Jane', age: 29 };

function filter(union: Union) {
if ('name' in union) { // Name
union.name; // string
}

if ('age' in union) { // Age
union.age; // number
}

if ('name' in union && 'age' in union) { // Name & Age
union.name;
union.age;
}
}
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

When you think about unions and intersections within TypeScript, it maps quite nicely to bend diagrams from set and they can be used to highlight an issue that you might have within your mental model for these particular types. So let's take a look. Consider two simple types, one with the property name of type string and one with the property age of type number. Now we can combine them in two ways. We can combine them into a union or we can combine them into an intersection. Now with the union, what we are trying to say is that the object will contain the properties of the name or it'll contain the properties of age.

00:33

And with an intersection we are saying that it must contain the properties of name as well as age. Now let's create some simple objects, name, age, as well as name and age to demonstrate the behaviors of these combination types. Now for the union, as you would expect, we can assign the name, object or the age object, but what you might not have expected is that we can also assign it the name and age object. The reason why this works of course, is because of the structural nature of TypeScript. That is an object containing name and age is also an object that contains the property name and it is therefore structurally compatible with this union.

01:09

And the intersection type on the other hand, is something that only accepts an object that contains both name as well as age. So if we try to assign it just the name or just the age, we will get a compiled time error. Now let's take a quick look at this from a set theory perspective, we have objects that have name, we have objects that have age, and then we have objects that have both name as well as age. And as we have seen as far as unions are concerned, all of these objects are perfectly valid. Whereas for an intersection, an object must support both of these properties and we only get a subset of the objects that might contain name or might contain age.

01:44

Now, even though that is a great mental model to have it is not entirely accurate. Let's reconsider this union of name or age. As we have seen, we can assign it objects that contain name or age or objects that contain name and age. However, this last case is actually just a lucky coincidence because of Typescripts structural typing. And this union is not designed to support this use case. We can demonstrate that when we try to filter out which particular member of the union and object currently belongs to, we accept the union and we check if it has the name property

02:17

and it'll filter it out to the name object. And we will be able to read the name property of type string. And if we check if it has an age property, we will successfully be able to filter out that ages of type number. However, if we check if it has both name as well as age, which is a valid object that we did assign, TypeScript will complain because as we have been saying, the union is designed to say that the object can be name or age. The case where we can assign an object containing both the properties is an unlucky side effect of Typescripts structural typing.

02:49

So if this is the mental model of for union types that you have, it's probably incorrect. It is more along the lines of this. Here we are saying that the object can either be named Or it can be age. And just coincidentally, an optic that contains both name and age is something that can be assigned to these, but we won't be able to read them safely with this mental model in place. Here's a thought experiment for you. How can you make the union fully support the use case where you do provide both the values? In other words, we don't want to see an error over here because clearly it is a valid object and I've intentionally sort

03:23

of given away the answer over here. If you want the union to support name and age, then we can add that to the union. And with this added, the error goes away because here's what the union looks like within Typescripts brain.

Professional TypeScript Masterclass

Professional TypeScript Masterclass

1.Introduction
free
⏱️ 1:54
2.Setup
free
⏱️ 5:44
3.Primitive Types
free
⏱️ 1:42
4.Instance Types
free
⏱️ 1:52
5.Arrays And Tuples
free
⏱️ 1:38
6.Objects
free
⏱️ 1:33
7.const declarations
free
⏱️ 1:03
8.Function Types
free
⏱️ 1:57
9.Structural Typing
free
⏱️ 2:10
10.Classes in TypeScript
free
⏱️ 1:48
11.Target Compiler Option
free
⏱️ 2:37
12.Generics
⏱️ 3:02
13.Special Types any And unknown
⏱️ 2:00
14.JavaScript to TypeScript
⏱️ 1:32
15.Frontend Projects
⏱️ 3:49
16.Type Assertions
⏱️ 2:15
17.Type Casting
⏱️ 1:16
18.Modules
⏱️ 1:55
19.Type Declarations
⏱️ 4:25
20.Creating NPM packages
⏱️ 3:20
21.Async Await
⏱️ 3:05
22.Running in NodeJS
⏱️ 1:40
23.Lexical this
⏱️ 2:34
24.readonly Modifier
⏱️ 1:59
25.Union Types
⏱️ 2:57
26.Literal Types
⏱️ 2:58
27.Type Narrowing
⏱️ 4:19
28.Discriminated Unions
⏱️ 3:29
29.Class Parameter Properties
⏱️ 1:02
30.Strict Compiler Option
⏱️ 6:18
31.null vs undefined
⏱️ 4:19
32.Intersection Types
⏱️ 2:03
33.Optional Modifier
⏱️ 2:47
34.Non Null Assertion Operator
⏱️ 3:40
35.Interfaces
⏱️ 2:28
36.Interface Declaration Merging
⏱️ 1:01
37.Types vs Interfaces
⏱️ 2:16
38.never Type
⏱️ 3:00
39.implements Keyword
⏱️ 1:25
40.Definite Assignment Assertion
⏱️ 2:31
41.User Defined Type Guards
⏱️ 2:02
42.Assertion Functions
⏱️ 3:42
43.Function Overloading
⏱️ 4:15
44.Call Signatures
⏱️ 2:53
45.Abstract Classes
⏱️ 1:53
46.Index Signatures
⏱️ 3:08
47.Readonly Arrays and Tuples
⏱️ 2:58
48.Double Assertions
⏱️ 2:20
49.const Assertions
⏱️ 3:55
50.this Parameter
⏱️ 2:33
51.Generic Constraints
⏱️ 2:43
52.typeof Type Operator
⏱️ 2:12
53.Lookup Types
⏱️ 3:12
54.keyof Type Operator
⏱️ 3:55
55.Conditional Types
⏱️ 4:39
56.Contitional Types with Unions and never
⏱️ 3:32
57.infer Keyword and `ReturnType<T>`
⏱️ 3:47
58.Mapped Types
⏱️ 2:48
59.Mapped Type Modifiers
⏱️ 3:37
60.Template Literal Type
⏱️ 4:28
61.Partial<T>
⏱️ 1:27
62.Required<T>
⏱️ 1:36
63.Readonly<T>
⏱️ 1:34
64.Record<K, T>
⏱️ 4:05
65.Project References
⏱️ 4:18
66.undefined vs. optional
⏱️ 2:48
67.satisfies Operator
⏱️ 2:42
68.PropertyKey Type
⏱️ 0:57
69.ThisType<T>
⏱️ 4:11
70.Awaited<T>
⏱️ 4:12
71.String Manipulation Types
⏱️ 3:36
72.Mapped Types as Clauses
⏱️ 4:01
73.Union vs Intersection Mental Model
⏱️ 3:36
74.Enums are Bad
⏱️ 8:11