Deep Dive into Typescript Types (Part I)

Typescript types have often been challenging for me since I started working on some frontend projects with Angular back a few years ago. Those complicated types are really hindering me from understanding the code. Not until I was recently working on a React project with react-hook-form library, did I become more aware of my weaknesses and strive to overcome them. This article will show some advanced TypeScript types frequently encountered in TypeScript projects, though it might not seem important if you aren't working with open-source projects.

To start off, let's explore some keywords and notations often encountered when dealing with Typescript types.

The extends keyword

The extends keyword has a different meaning when used with TypeScript types. As a keyword in TypeScript types, it's used for conditional checking and therefore often appears with a ternary operator. The example below shows transformation of a boolean type into either 1 or 0.

type LogicGate<T extends boolean> = T extends true ? 1 : 0;
const result: boolean = true;

type Result = LogicGate<typeof result>; // type Result = 1;

The infer keyword

The keyword infer appears quite often in react-hook-form library's types. Even though it appears almost everywhere in d.ts files, it has only one use: to store the resultant type in a variable named after the keyword infer. One of the intuitive examples would be the built-in Typescript ReturnType.

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

// 1st example
const func1 = () => 'Hello';
type T1 = ReturnType<typeof func1>; // type T1 = string

// 2nd example
const func2 = (v: number) => v + 1;
type T2 = ReturnType<typeof func2>; // type T1 = number

// 3rd example
type ExtractedArrayType<T extends readonly any[]> = T extends readonly (infer U)[] ? U : never;
const arr = [1, 2, 3]
type T3 = ExtractedArrayType<typeof arr> // type T3 = number

// 4th example
type Flatten<T> = T extends (infer U)[] ? Flatten<U> : T;
const arr2 = [[1, [2], [[3]]], 4]
type T4 = Flatten<typeof arr2> // type T4 = number

Here, we capture the return type of an arbitrary function and name it R, which can be used afterwards.

Noticed that third and fourth example are rather complicated, The third example extracts the type from an array while the fourth one recursively extracts the type from a nested array