DEV Community

Audrey Kadjar
Audrey Kadjar

Posted on

25 2 1 2 3

🛠️🧩 TypeScript Utility Types - a cheat sheet

Utility types look complicated, but they are actually simple and super handy. Let's say you have a type and you want to customize it for a use case. Instead of duplicating that type and modifying it to fit that use case, you can use a utility type.

Here is a recap of some utility types. You can play with the examples on the TypeScript Playground.

Partial

This utility type makes all properties of a type optional. This is useful if you need to update or extend a type, but don't need to provide every property. However, TypeScript still enforces correct types for each property and does not allow arbitrary properties-all properties must be included in the type definition.

interface User {
  name: string;
  age: number;
}

const user1: Partial<User> = { name: "Audrey" }; // age is optional
const user2: Partial<User> = { age: 33 }; // name is optional
const user4: Partial<User> = { name: 33 }; // Error: Type 'number' is not assignable to type 'string'
const user5: Partial<User> = { whatever: 33 }; // Error: Object literal may only specify known properties
const user6: Partial<User> = {}; // can be empty since it makes all properties optional
Enter fullscreen mode Exit fullscreen mode

Required

The opposite of Partial, it makes all properties of a type mandatory. This can be useful when you want to make sure that a type has all of its properties set, even if it was previously optional.

interface Shape {
  color?: string;
  width?: number;
}

const shape1: Required<Shape> = { color: "green", width: 40 }; // both properties are mandatory
const shape2: Required<Shape> = {color: "blue"} //Error: Property 'width' is missing in type '{ color: string; }' but required in type 'Required<Shape>'

Enter fullscreen mode Exit fullscreen mode

Readonly

This makes all properties of a type read-only, meaning they can't be reassigned after creation.

interface User {
  name: string;
  age: number;
}

const user: Readonly<User> = { name: "Alice", age: 25 };
user.age = 26; // Error: Cannot assign to 'age' because it is a read-only property
Enter fullscreen mode Exit fullscreen mode

Record

This creates an object type with keys of type K and values of type T. It’s useful when you want to enforce a shape for an object with dynamic keys.

type Role = "admin" | "user" | "guest";

const userRoles: Record<Role, string> = {
  admin: "Administrator",
  user: "Regular User",
  guest: "Guest User",
};
Enter fullscreen mode Exit fullscreen mode

Pick

This type constructs a new type by picking a set of properties K from type T. It’s useful when you need a subset of properties from an existing type.

In this example, pickedUser object is correctly typed because it includes only the name property. It is then assigned to an object, which matches the expected type.

interface User {
  name: string;
  age: number;
}

const pickedUser: Pick<User, "name"> = { name: "Audrey" };
const pickedUser2: Pick<User, "name"> = { name: "Audrey", age: 33 }; // Error: 'age' does not exist in type 'Pick<User, "name">'
Enter fullscreen mode Exit fullscreen mode

What do utility types compile to?

Because TypeScript is a superset of JavaScript, utility types exist only at compile time to enforce type safety and do not affect runtime output. The final JavaScript output is just plain JavaScript objects with no TypeScript type constraints.

The examples above compile to:

"use strict";
//Partial
const user1 = { name: "Audrey" }; 
const user2 = { age: 33 }; 
const user6 = {}; 

//Required
const shape1 = { color: "green", width: 40 };

//Readonly
const user = { name: "Alice", age: 25 };

//Record
const userRoles = {
    admin: "Administrator",
    user: "Regular User",
    guest: "Guest User",
};

//Pick 
const pickedUser = { name: "Audrey" };
Enter fullscreen mode Exit fullscreen mode

Make your own utility types

TypeScript enables the creation of custom utility types using generics and mapped types. For instance, you can define your own version of Partial.

The keyof T type in TypeScript represents all the property names (keys) of T. In the example, [K in keyof T] iterates over each key, while T[K] ensures the property retains its original type, and ? makes each property optional.

The custom MyPartial<T> type behaves similarly to Partial<T>, making all properties optional.

type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

interface User {
  name: string;
  age: number;
}

const user: MyPartial<User> = { name: "Alice" }; // 'age' is optional
Enter fullscreen mode Exit fullscreen mode

I hope this was helpful! You can find all the available utility types in the TypeScript documentation.

Feel free to reach out if you have any questions! You can also find me on Github, BlueSky, LinkedIn, and Instagram.

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (3)

Collapse
 
juan_labrada_42e0d23118f4 profile image
Juan Labrada

Excellent article!! I was looking exactly for this.

Collapse
 
audreyk profile image
Audrey Kadjar
Collapse
 
boriscy profile image
Boris Barroso

Really good one.

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay