Ch. 2 · TypeScript

TypeScript Key Remapping in Mapped Types

Rename mapped-type keys with an as clause, filter keys by mapping to never, and build getter and prefixed types generically.

~2 min readadvancedupdated Oct 5, 2026

A mapped type normally keeps the source keys. The as clause remaps each key using a template literal or conditional type, which lets you synthesize new names — prefixes, suffixes, getters — or drop keys by mapping them to never.

Before you start

You should be comfortable with mapped types, keyof, and template literal types. This article extends basic mapped types with key remapping.

Step-by-step walkthrough

Step 1: Rename with a template literal

{ [K in keyof T as get${Capitalize<string & K>}]: () => T[K] } produces getter methods for each property. The string & K intersection is required because keyof T can include symbols, and Capitalize accepts only string types.

Step 2: Filter keys by mapping to never

Returning never from the as clause removes that key entirely. { [K in keyof T as T[K] extends string ? K : never]: T[K] } keeps only string-valued properties, which is how Pick-style helpers can be expressed generically.

Step 3: Avoid key collisions

Remapping can make two source keys produce the same result, and TypeScript collapses them, often with a confusing result. Keep the transform injective — a prefix or suffix preserves distinctness, while stripping characters may not.

Worked scenario

The getter type is synthesized from the property names.

interface User { name: string; age: number }
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
declare const getters: UserGetters;
const name: string = getters.getName();
void name;
TypeScript

Walk through the example

Getters<User> expands to { getName: () => string; getAge: () => number }. The as clause renamed name to getName and age to getAge, and the value type T[K] was preserved. A caller must implement both getters, and the return types follow the original property types automatically.

Common mistake

Forgetting that keyof T includes symbol keys, so Capitalize<K> errors until you intersect string & K. Another is remapping keys onto the same name, which silently merges them and drops one value type, producing a type that no longer matches the source.

Verify the behavior

Apply Getters to an interface and assert the generated names and return types by assigning each to a typed variable. Filter keys to only string properties and confirm the number property disappears. Try a transform that collides two keys and observe the merged result, which shows why injective naming matters.

Interview exercise

You want a type that adds a set- prefixed setter for every property while keeping the originals. How do you express it?

Answer and reasoning

Intersect the original type with a remapped one: T & { [K in keyof T as set${Capitalize<string & K>}]: (value: T[K]) => void }. The remapped side generates the setters, and the intersection keeps the original properties. Because the prefix is injective, the new keys cannot collide with the existing ones.

Continue learning

Extend this with mapped type modifiers and template literal types. Read the TypeScript mapped types handbook and try the TypeScript MCQs.

More in TypeScript

read ✓TypeScript · hard

TypeScript Abstract Classes and Contracts

Share behavior with abstract classes, enforce required members, and decide when an interface or composition is the better contract.

~2 min readread →
read ✓TypeScript · mid

TypeScript Enums vs Union Types

Compare enums with unions of string literals: runtime cost, serialization, exhaustiveness and which one fits application code.

~3 min readread →
esc