Master TypeScript with Mapped Types - Enhance Your Code Quality

A cool trick to write better TypeScript code is to use mapped types.

What is Mapped types

Mapped types allow you to create new types by transforming properties of existing types, making your code more flexible, and less prone to errors. Let’s take a look at an example:

Suppose you have a type representing a User:

1
2
3
4
5
6
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

Now, you want to create a new type with optional properties based on the User type. You can use a mapped type to achieve this:

1
2
3
4
5
type Optional<T> = {
  [P in keyof T]?: T[P];
};

type OptionalUser = Optional<User>;

OptionalUser will now have the same properties as User, but all of them will be optional:

1
2
3
4
5
6
7
8
9
const user1: OptionalUser = {
  id: 1,
  name: "Alice",
};

const user2: OptionalUser = {
  id: 2,
  email: "bob@example.com",
};

You can even go further and create a utility type to make any property of a given type readonly:

1
2
3
4
5
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type ReadonlyUser = Readonly<User>;

Now you have a ReadonlyUser type where all properties are immutable:

1
2
3
4
5
6
7
8
9
const user3: ReadonlyUser = {
  id: 3,
  name: "Carol",
  email: "carol@example.com",
  age: 30,
};

// This will produce a TypeScript error, as the name property is readonly:
user3.name = "Catherine";

Mapped types can help you create more versatile and maintainable TypeScript code by reducing duplication and allowing for easy transformation of existing types.

P.S. Can you identify the TypeScript utility type that actually accomplishes this? (Special thanks to my coworker @thomasnotfound for bringing this to my attention.) Cheer! 🍺