Mapped Types in TypeScript let you transform one type to another, by transforming the keys from one type to another. TypeScript offers a lot of flexibility with this, allowing you to modify the name of types, perform string interpolation on keys and more. In this article, I’ll explain their utility, with a few useful examples to help get you started.

An example of this would be something like user accounts. You might have something like this:

type User = {
    name: string;
    age: number;
    email: string;
    password: string;
    friends: User[];
};

You might want for example to have admin permissions to say whether or not an admin account can modify each of these properties.

type UserPermissions = {
    name: boolean;
    age: boolean;
    email: boolean;
    password: boolean;
    friends: boolean;
};

This is where mapped types help, and let you create this type by transforming the User type.

Remaking the Partial Type

TypeScript contains a lot of utility types, including the Partial type. This transforms every one of the properties of your type to be optional. If you wanted to implement this manually, you’d have to rewrite your type and copy every property, changing each one to be optional. You’d then have to keep up with this dependency, making sure they stay aligned, and repeat this every time you want a similar pattern.

Taking our previous example again, you might create something like a UserChange type that might store some properties of a user account to change, but not necessarily all of them.

Here’s my recreation of the Partial type using a mapped type.

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

We can break this up into parts.

  • MyPartial<T> gives us a generic type with T as a type parameter.
  • keyof T gives us all of the keys of a type as a type union, and Property in lets us iterate through these
  • We then make the parameter optional using “?”
  • T[Property] gives us back the type that we get when accessing the property in our original type, e.g. “What type do I get back when I access User.name”, which would be a string.

Then to create our UserChange type:

type UserChange = MyPartial<User>;

Which is the same as:

type UserChange = {
    name?: string;
    age?: number;
    email?: string;
    password?: string;
    friends?: User[];
};

With one line we get the type we need, and some self-documenting code!

Key Remapping

You can use the “as” keyword to rename the keys for your mapped types. This can be combined with template literal types, which lets you modify key names like strings.

Let’s expand our UserPermissions example to add keys for reading and writing each user property:

type Permissions = 'read' | 'write';

type UserPermissions = {
    [Property in keyof User as `${Permissions}${Capitalize<Property>}`]: boolean;
};

Breaking this up again, we have:

  • For each property in User, get the key.
  • Use the capitalize utility type just to keep our key in camelCase, e.g. “name -> Name”.
  • Then using a template literal type we can add a prefix to our property, e.g. “Name -> readName”.
  • Since we’re using a type union in this, TypeScript builds every combination of “read/write” and each user property.

So from a few lines, we end up with a type that looks like this:

type UserPermissions = {
    readName: boolean;
    writeName: boolean;
    readAge: boolean;
    writeAge: boolean;
    readEmail: boolean;
    writeEmail: boolean;
    readPassword: boolean;
    writePassword: boolean;
    readFriends: boolean;
    writeFriends: boolean;
};

You could implement this manually, but the benefit of this is not having to worry about keeping anything synchronised. You can add new permissions, new fields, and have the changes reflected instantly!

Thanks for reading this introduction to mapped types in TypeScript. They’re a powerful tool that really showcase the benefits of TypeScript over other typed languages. Feel free to leave a comment below if you liked the article, if you had any issues, or maybe with any clever ways you’ve seen mapped types implemented in code!

Avatar photo
👋 Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, reach out on Twitter at @marile0n

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.