99

Concurrent visitors over the last 30 minutes. (~)

Peak concurrent visitors -
Yesterday: 0 / Today: 0

TypeScript Utility Types in Depth

TypeScript Utility Types in Depth

TypeScript provides a variety of built-in utility types that you can use to transform and manipulate types.
With them, you can make your code more reusable and write code that is more concise and easier to read.

Utility Description (main type) Type variables
Partial Returns a new type with every property of TYPE made optional (interface) <TYPE>
Required Returns a new type with every property of TYPE made required (interface) <TYPE>
Readonly Returns a new type with every property of TYPE made readonly (interface) <TYPE>
Record Returns a new type that uses KEY as its properties and TYPE as the type of their values (interface) <KEY, TYPE>
Pick Returns a new type with the properties picked from TYPE by KEY (interface) <TYPE, KEY>
Omit Returns a new type that omits the properties in KEY from TYPE and keeps the rest (interface) <TYPE, KEY>
Exclude Returns a new type with TYPE2 excluded from TYPE1 (union) <TYPE1, TYPE2>
Extract Returns a new type with TYPE2 extracted from TYPE1 (union) <TYPE1, TYPE2>
NonNullable Returns a new type with null and undefined excluded from TYPE (union) <TYPE>
Parameters Returns the parameter types of TYPE as a new tuple type (function, tuple) <TYPE>
ConstructorParameters Returns the parameter types of TYPE as a new tuple type (class, tuple) <TYPE>
ReturnType Returns the return type of TYPE as a new type (function) <TYPE>
InstanceType Returns the instance type of TYPE (class) <TYPE>
ThisParameterType Returns the type of the explicit this parameter of TYPE as a new type (function) <TYPE>
OmitThisParameter Returns a new type with the explicit this parameter of TYPE removed (function) <TYPE>
ThisType Specifies the this context of TYPE and returns nothing! (interface) <TYPE>
Awaited Returns the resolve type of a Promise <TYPE>
NoInfer Blocks type inference for the generic type T <TYPE>
Uppercase Converts every character of the string literal TYPE to uppercase <TYPE>
Lowercase Converts every character of the string literal TYPE to lowercase <TYPE>
Capitalize Converts the first character of the string literal TYPE to uppercase <TYPE>
Uncapitalize Converts the first character of the string literal TYPE to lowercase <TYPE>

# Partial

Returns a new type with every property of TYPE made optional (?).

TS
1
Partial<TYPE>
TS
1
2
3
4
5
6
7
8
9
10
11
12
interface User { name: string age: number } const userA: User = { // TS2741: Property 'age' is missing in type '{ name: string }' but required in type 'User'. name: 'A' } const userB: Partial<User> = { name: 'B' }

You can think of Partial<User> in the example above like this.

TS
1
2
3
4
interface NewType { name?: string age?: number }

# Required

Returns a new type with every property of TYPE made required.

TS
1
Required<TYPE>
TS
1
2
3
4
5
6
7
8
9
10
11
12
interface User { name?: string age?: number } const userA: User = { name: 'A' } const userB: Required<User> = { // TS2741: Property 'age' is missing in type '{ name: string }' but required in type 'Required<User>'. name: 'B' }

You can think of Required<User> in the example above like this.

TS
1
2
3
4
interface User { name: string age: number }

# Readonly

Returns a new type with every property of TYPE made readonly (readonly).

See the 'Interfaces > Readonly properties' section.

TS
1
Readonly<TYPE>
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface User { name: string age: number } const userA: User = { name: 'A', age: 12 } userA.name = 'AA' // OK! const userB: Readonly<User> = { name: 'B', age: 13 } userB.name = 'BB' // TS2540: Cannot assign to 'name' because it is a read-only property.

You can think of Readonly<User> in the example above like this.

TS
1
2
3
4
interface NewType { readonly name: string readonly age: number }

# Record

Returns a new object type that uses KEY as its properties (keys) and TYPE as the type of their values.

TS
1
Record<KEY, TYPE>
TS
1
2
3
4
5
6
type Name = 'neo' | 'lewis' const developers: Record<Name, number> = { neo: 12, lewis: 13 }

You can think of Record<Name, number> in the example above like this.

TS
1
2
3
4
interface NewType { neo: number lewis: number }

Record is useful when you need a loose object type.

TS
1
2
3
4
5
6
7
8
9
10
11
function getValues(obj: Record<string, unknown>, keys: string[]) { return keys.map(key => obj[key]) } const user = { name: 'Neo', age: 22, isValid: true, email: 'neo@gmail.com' } console.log(getValues(user, ['name', 'email'])) // ['Neo', 'neo@gmail.com']

# Pick

Returns a new object type with the properties picked from TYPE by KEY.
You can pick several properties with a union type.

TS
1
Pick<TYPE, KEY>
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
interface User { name: string age: number email: string isValid: boolean } type Key = 'name' | 'email' const user: Pick<User, Key> = { name: 'Neo', email: 'thesecon@gmail.com', age: 22 // TS2322: Type '{ name: string; email: string; age: number; }' is not assignable to type 'Pick<User, Key>'. }

You can think of Pick<User, Key> in the example above like this.

TS
1
2
3
4
interface NewType { name: string email: string }

# Omit

The opposite of Pick above: it returns a new object type that omits the properties in KEY from TYPE and keeps the rest.
You can omit several properties with a union type.

TS
1
Omit<TYPE, KEY>
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
interface User { name: string age: number email: string isValid: boolean } type Key = 'name' | 'email' const user: Omit<User, Key> = { age: 22, isValid: true, name: 'Neo' // TS2322: Type '{ age: number; isValid: true; name: string; }' is not assignable to type 'Pick<User, "age" | "isValid">'. }

You can think of Omit<User, Key> in the example above like this.

TS
1
2
3
4
5
6
interface NewType { // name: string age: number // email: string isValid: boolean }

# Exclude

Returns a new type with the union TYPE2 excluded from the union TYPE1.

TS
1
Exclude<TYPE1, TYPE2>
TS
1
2
3
4
5
6
7
type T = string | number | boolean | string[] const a: Exclude<T, number | string[]> = 'Only string' const b: Exclude<T, number> = 1234 // Type Error! ts(2322) const c: T = 'Hello world!' const d: T = 1234 console.log(a, b, c, d)

# Extract

Returns a new type with the union TYPE2 extracted from the union TYPE1.

TS
1
Extract<TYPE1, TYPE2>
TS
1
2
3
4
5
6
7
type T = string | number | string[] type U = number | boolean // Extract<T, U> is number const a: Extract<T, U> = 123 const b: Extract<T, U> = 'Only number' // Type Error! ts(2322) console.log(a, b)

# NonNullable

Returns a new type with null and undefined excluded from the union TYPE.

TS
1
NonNullable<TYPE>
TS
1
2
3
4
type T = string | number | undefined const a: T = undefined const b: NonNullable<T> = null // TS2322: Type 'null' is not assignable to type 'string | number'.

# Parameters

Returns the parameter types of the function TYPE as a new tuple type.

TS
1
Parameters<TYPE>
TS
1
2
3
4
5
6
7
8
9
10
function fn(a: string | number, b: boolean) { console.log(a, b) } // Parameters<typeof fn> is [string | number, boolean] const a: Parameters<typeof fn> = ['Hello', true] const b: Parameters<typeof fn> = ['Hello', true] const c: Parameters<typeof fn> = [123, false] const d: Parameters<typeof fn> = [123, 456] // Type Error! ts(2322) console.log(a, b, c, d)

# ConstructorParameters

Returns the parameter types of the class TYPE as a new tuple type.

TS
1
ConstructorParameters<TYPE>
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User { constructor( public name: string, private age: number ) {} } // ConstructorParameters<typeof User> is [string, number] const a: ConstructorParameters<typeof User> = ['Neo', 12] const b: ConstructorParameters<typeof User> = ['Lewis'] // Type Error! ts(2741) console.log(a, b) const neo = new User('Neo', 12) const lewis = new User('Lewis', 34) console.log(neo, lewis)

# ReturnType

Returns the return type of the function TYPE as a new type.

TS
1
ReturnType<TYPE>
TS
1
2
3
4
5
6
function fn(str: string) { return str } const a: ReturnType<typeof fn> = 'Only string' const b: ReturnType<typeof fn> = 1234 // TS2322: Type '123' is not assignable to type 'string'.

# InstanceType

Returns the instance type of the class TYPE.

TS
1
InstanceType<TYPE>
TS
1
2
3
4
5
class User { constructor(public name: string) {} } const neo: InstanceType<typeof User> = new User('Neo')

# ThisParameterType

Returns the type of the explicit this parameter of the function TYPE as a new type.
If the function TYPE has no explicit this parameter, it returns the unknown type.

See the 'Functions > this > Explicit this' section.

TS
1
ThisParameterType<TYPE>

In the following example, the explicit this type of the function toHex is Number, and we use that type to declare the type of the parameter n of the function numberToString.
This prevents a this of a different type from being bound to toHex.

TS
1
2
3
4
5
6
7
8
9
// https://www.typescriptlang.org/docs/handbook/utility-types.html#thisparametertype function toHex(this: Number) { return this.toString(16) } function numberToString(n: ThisParameterType<typeof toHex>) { return toHex.apply(n) }

# OmitThisParameter

Returns a new type with the explicit this parameter of the function TYPE omitted (removed).

TS
1
OmitThisParameter<TYPE>

In the following example, the function getAge is designed around the data cat, so it can't use the new data dog, which has a different type, as this.
However, OmitThisParameter lets you create a new function type without the explicit this, so you can use the data dog without changing getAge itself.

Note that this.age can now hold any value!

TS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Original object const cat = { age: 12 // Number } function getAge(this: typeof cat) { return this.age } getAge.call(cat) // 12 // New object const dog = { age: '13' // String } getAge.call(dog) // Type Error! ts(2345) // New function type without this const getAgeForDog: OmitThisParameter<typeof getAge> = getAge getAgeForDog.call(dog) // 13

# ThisType

Specifies the this context of TYPE and doesn't return a separate type.

TS
1
ThisType<TYPE>

The method getName, which is passed as an argument to the function makeNeo, uses this.name inside, so we set the this context explicitly with ThisType.
However, since ThisType doesn't return a separate type, the type of the return value of makeNeo ({ name: 'Neo', ...methods }) isn't inferred correctly.
So you need a separate type assertion such as as User before you can call neo.getName correctly.

TS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface User { name: string getName: () => string } function makeNeo(methods: ThisType<User>) { return { name: 'Neo', ...methods } as User } const neo = makeNeo({ getName() { return this.name } }) neo.getName() // Neo

# Awaited

The Awaited utility type returns the resolved type of a Promise.
If Promises are nested, it finds the resolved type recursively and returns it.

TS
1
Awaited<TYPE>
TS
1
2
3
4
5
6
7
8
9
10
11
// A is string type A = Awaited<Promise<string>> // B is number type B = Awaited<Promise<Promise<number>>> // C is string | number type C = Awaited<Promise<string> | Promise<number>> // D is string | number type D = Awaited<Promise<string> | number>

A typical use is getting the type of API response data.
For example, say you have an asynchronous function that returns user information from an API (Promise<User>), like this.

/api/user.ts
TS
1
2
3
4
5
6
7
8
9
interface User { id: number name: string email: string } export async function fetchUser(id: number): Promise<User> { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`) return res.json() }

You can get the type of the user data returned by the fetchUser function with the Awaited utility type.
Get the type of the function with typeof fetchUser, get its return type with ReturnType, and then get the resolved type with Awaited.
This way, even if the return value of the API function (fetchUser) changes, the User type updates automatically, which makes maintenance easier.

/main.ts
TS
1
2
3
4
5
6
7
8
9
10
11
import { fetchUser } from '@/api/user' // The return type of the `fetchUser` function is `Promise<User>`. type User = Awaited<ReturnType<typeof fetchUser>> function getUserName(user: User) { return user.name } const user = await fetchUser(1) getUserName(user)

# NoInfer

The NoInfer utility type stops the inference of a generic type variable from being widened unintentionally.
This lets you block type inference at a specific position and make it rely on the type given at another position.

TS
1
NoInfer<TYPE>

The getItem function in the following example takes a readonly array and a default item.
If the array includes the default item (defaultItem), it returns that default item; if not, it returns the first item of the array instead.
This function works fine at runtime, but no type error occurs even when the second argument isn't one of the array items.
That's because, in a call like getItem(colors, 'blue'), TypeScript uses the type variable T of defaultItem to widen the inferred type to "red" | "yellow" | "green" | "blue", including the value blue that isn't in colors.

TS
1
2
3
4
5
6
7
8
function getItem<T>(items: readonly T[], defaultItem?: T) { if (!defaultItem) return items[0] return items.includes(defaultItem) ? defaultItem : items[0] } const colors = ['red', 'yellow', 'green'] as const console.log(getItem(colors, 'yellow')) // 'yellow' console.log(getItem(colors, 'blue')) // 'red'

Now let's use the NoInfer utility type so that a type error occurs when the second argument isn't one of the array items.
Change the type of defaultItem to NoInfer<T> so that its type variable T isn't used to widen the inferred type.
Then a type error occurs when the second argument isn't one of the array items.

TS
1
2
3
4
5
6
7
8
9
10
11
function getItem<T>(items: readonly T[], defaultItem?: NoInfer<T>) { // ... } const colors = ['red', 'yellow', 'green'] as const console.log(getItem(colors, 'yellow')) // 'yellow' console.log(getItem(colors, 'blue')) // Type Error! const numbers = [7, 8, 9] as const console.log(getItem(numbers, 8)) // 8 console.log(getItem(numbers, 100)) // Type Error!

# Uppercase

Returns a new type with every character of TYPE converted to uppercase.

TS
1
Uppercase<TYPE>
TS
1
2
3
4
5
6
7
type Str = `hello ${string}!` type UpperStr = Uppercase<Str> const str: Str = 'hello world!' const upperStr1: UpperStr = 'HELLO WORLD!' const upperStr2: UpperStr = 'HELLO world!' // Type Error! console.log(str, upperStr1, upperStr2)

# Lowercase

Returns a new type with every character of TYPE converted to lowercase.

TS
1
Lowercase<TYPE>
TS
1
2
3
4
5
6
7
type Str = `HELLO ${string}!` type LowerStr = Lowercase<Str> const str: Str = 'HELLO WORLD!' const lowerStr1: LowerStr = 'hello world!' const lowerStr2: LowerStr = 'HELLO world!' // Type Error! console.log(str, lowerStr1, lowerStr2)

# Capitalize

Returns a new type with the first character of the string literal TYPE converted to uppercase.

TS
1
Capitalize<TYPE>
TS
1
2
3
4
5
6
7
type Str = `hello ${string}!` type CapStr = Capitalize<Str> const str: Str = 'hello world!' const capStr1: CapStr = 'Hello world!' const capStr2: CapStr = 'hello world!' // Type Error! console.log(str, capStr1, capStr2)

# Uncapitalize

Returns a new type with the first character of the string literal TYPE converted to lowercase.

TS
1
2
3
4
5
6
7
type Str = `HELLO ${string}!` type UncapStr = Uncapitalize<Str> const str: Str = 'HELLO WORLD!' const uncapStr1: UncapStr = 'hELLO WORLD!' const uncapStr2: UncapStr = 'HELLO WORLD!' // Type Error! console.log(str, uncapStr1, uncapStr2)