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;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.