Skip to Content
Course content

63: Key Remapping in Mapped Types

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I once spent an entire afternoon fighting with a legacy API that had a very peculiar requirement: every property I sent to the server had to be prefixed with set_, and every property I read back had to start with get_. I had my clean User interface, and I tried to use a standard mapped type to create these request types. I quickly hit a wall. I could change the value of the properties to be strings or numbers, but I couldn't change the name of the keys themselves. I felt like I was staring at a locked door until I discovered key remapping.

Up until now, you've used mapped types to iterate over keys and change their types—essentially saying, "For every key in this object, make its value a boolean." Key remapping allows you to say, "For every key in this object, change the key's name to this, and then make its value a boolean." It's the difference between painting the walls of a room and actually moving the walls.

Transforming Keys with the as Clause

The magic happens with the as keyword inside the mapped type's key definition. Instead of just [K in keyof T], you add as NewKeyName. This tells TypeScript to map the original key to a new identity before assigning the value type.

type User = {
  name: string;
  email: string;
  age: number;
};

// We want to turn { name: string } into { getName: () => string }
type Getters = {
  [K in keyof T as `get${Capitalize}`]: () => T[K];
};

type UserGetters = Getters<User>;
/* 
Result: {
  getName: () => string;
  getEmail: () => string;
  getAge: () => number;
}
*/

Notice how I used string & K? That's a little trick to tell TypeScript that K is definitely a string, which lets us use template literal types. If you don't do that, TypeScript might complain that K could be a symbol, and you can't interpolate a symbol into a string template.

Filtering Keys Out of a Type

One of the most powerful, and slightly less obvious, uses of key remapping is filtering. Since the as clause can map a key to never, TypeScript will simply drop that key from the resulting object. I use this constantly when I need to strip out specific types of properties from a large API response before passing them to a component.

type Product = {
  id: string;
  title: string;
  price: number;
  internalCode: string;
  lastUpdated: Date;
};

// Only keep keys that are strings
type StringOnly = {
  [K in keyof T as string extends T[K] ? K : never]: T[K];
};

type ProductStrings = StringOnly<Product>;
/* 
Result: {
  id: string;
  title: string;
  internalCode: string;
}
(price and lastUpdated are gone!)
*/

It feels a bit like a hack at first, but once you realize you can use a ternary operator inside the as clause, you've essentially gained the ability to "filter" your types. It's significantly cleaner than trying to manually define a bunch of partial types every time the backend team adds a new field to a model.




📋 Practical Task

Building a Feature Permission Mapper

Imagine you are building a dashboard where different features are enabled based on user roles. You have a base type that lists the features of your application, but your permission system requires these keys to be transformed into "can_" prefixed booleans.

Your Task:

  • Create a type called AppFeatures with the following keys: editProfile, deleteUser, viewAnalytics, and manageBilling (all should be strings for the base type).
  • Create a mapped type called Permissions that takes a type T and transforms every key by prefixing it with can_ and changing the value type to a boolean.
  • Apply the Permissions type to AppFeatures to create a type called UserPermissions.

Your final UserPermissions type should look like this: { can_editProfile: boolean; can_deleteUser: boolean; ... }

Rating
0 0

There are no comments for now.

to be the first to leave a comment.