Union Types

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 Types

Subscription Required

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

Using any as a hack

/**
* @param input a command or an array of commands
* @returns a single trimmed string
*/
function formatCommandline(input: any) {
let line = '';
if (typeof input === 'string') {
line = input.trim();
} else {
line = input.map(x => x.trim()).join(' ');
}
return line;
}

console.log(formatCommandline('hello ')); // 'hello'
console.log(formatCommandline(['hello ', ' world '])); // 'hello world'

// Allows unsafe code that fails at Runtime 😬
console.log(formatCommandline(1337)); // Runtime Error

Introducing Unions

For example string | string[]:

/**
* @param input a command or an array of commands
* @returns a single trimmed string
*/
function formatCommandline(input: any) {
let line = '';
if (typeof input === 'string') {
line = input.trim();
} else {
line = input.map(x => x.trim()).join(' ');
}
return line;
}

console.log(formatCommandline('hello ')); // 'hello'
console.log(formatCommandline(['hello ', ' world '])); // 'hello world'

// Caught at compile time!
console.log(formatCommandline(1337)); // Invalid Input!

Another Example

/**
* Takes a string and adds `padding` to the left.
* If `padding` is a number, then that number of spaces is added to the left.
* If `padding` is a string, then `padding` is appended to the left.
*/
function padLeft(value: string, padding: number | string) {
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value;
}
if (typeof padding === 'string') {
return padding + value;
}
throw new Error(`Expected number or string, got '${padding}'.`);
}

padLeft('Hello world', 4); // ' Hello world'
padLeft('Hello world', ' '); // ' Hello world'
padLeft('Hello world', '---'); // '---Hello world'

padLeft('Hello world', false); // Caught at Compile Time!

Using Type Aliases

Single line:

type Padding = number | string;

Multi line (leading | is allowed):

type Padding =
| number
| string;
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

One amazing feature within the TypeScript type system is type unions. Let's consider a simple example to demonstrate their motivation. Here we have a simple function that can operate on a string or an array of strings, and within the function body, if it is simply a string, it'll trim it and return the result. And if it is an array, it'll trim each of the strings in the array and then join them with the space and return the results. Such functions, which operate on a single item or an array of items is quite common within the JavaScript ecosystem.

00:33

Currently, in order to accept a string as well as an array of strings, we have this function annotated as input type. Any however, this results in type unsafety. For example, someone can pass in a number and now our code will blow up at runtime without any compiled time errors. Fortunately, TypeScript allows you to create types as a union of predefined types. For example, in our case, we want to accept only a string or an array of strings once we've added this annotation. Invalid usages are highlighted

01:06

as a compile time error per type script. Now, union types are simply a set of types separated by the pipe operator as shown over here, you are free to use any type names separated by the pipe operator to create a union type. Let's demonstrate that by another usage. A common requirement in UR programming is to pair a string with some characters before it. Here we have a function that achieves this objective. If the input argument is a number, it pads with a number of spaces. Alternatively, you can pass in your own padding string,

01:40

and that is used prepend it before the input value. And if the input is not the number or a string, this function throws a runtime error. A naive approach would be to accept all inputs using the unknown type as we have done over here. And indeed, it does achieve the objective of accepting a number or a padding string. However, just like any unknown accepts all types, and if someone were to call this function with something that is not a number or a string, for example, a bullion, this function will throw a runtime exception. Fortunately, once more, we can catch this error

02:14

by using a proper union type annotation for the pairing parameter. In this case, we want to accept a number or a string, and anything else will be caught as a compile time error from TypeScript as shown over here. Now, just like any other type annotation within TypeScript, you can extract a union type into its own type alias, giving it a well-defined name for better code readability. Now, if your unions are becoming a bit long, TypeScript allows you to split your union into multiple lines. Also, in order to increase the readability a bit more. TypeScript allows you to optionally add a pipe

02:48

before the first member in the union. This has no impact on the semantics of the type and is purely there for code readability purposes.

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