How Type Narrowing Works in Typescript

Updated at

Type narrowing is the process in which typescript can clearly determine that an entity has a certain type.

A very basic example would be:

0type MyType = string | number
1
2function myFunction(prop: MyType) {
3    if(typeof prop === "number") {
4        // handle it like a number
5    } else {
6        // handle it like a type
7    }
8}

However, a more sophisticated example for type narrowing in typescript might look like this:

Assume that an API returns a status field that determines which other fields will be present in the response. In the first image, you can see a response where status: "VALID" includes fieldA, and another response where status: "INVALID" includes fieldB. Both might include a sharedFieldC, but it is optional.

0const responseA: Response = {
1    status: "VALID",
2    fieldA: false
3}
4
5const responseB: Response = {
6    status: "INVALID",
7    fieldB: false
8}

To clearly define this behavior in TypeScript, you can create an abstract type for shared fields and then define explicit variants of your response for each individual status.

In the second image, TypeScript uses type narrowing in the if/switch statement to infer which fields are available based on the status field.

0type AbstractApiResponse = {
1    status: "VALID" | "INVALID"
2    sharedFieldC?: boolean
3}
4
5type ValidResponse = AbstractApiResponse & {
6    status: "VALID",
7    fieldA: boolean
8}
9
10type InvalidResponse = AbstractApiResponse & {
11    status: "INVALID",
12    fieldB: boolean
13}
14
15type Response = InvalidResponse | ValidResponse
16
17function responseHandler(response: Response) {
18    if(response.status === "VALID") {
19        response.fieldA
20        response.fieldB // This will not compile
21    } else {
22        response.fieldA // This will not compile
23        response.fieldB
24    }
25}

What Do You Gain From Type Narrowing In Typescript?

  • No need for assertion operators like ! or ?
  • Simple setup for a clearly defined API response structure
  • Improved readability by using TypeScript's type inference