Q207: Mapped Types and Key Remapping
What Interviewers Want To Evaluate
Interviewers want to know whether you understand how TypeScript can transform object types programmatically.
They check mapped types, keyof, indexed access types, modifiers, key remapping with as, and practical cases like form state, event handlers, permissions, and configuration objects.
Short Interview Answer
Mapped types iterate over keys of another type to produce a related type. They power many utility types like Partial, Readonly, and Record. I use mapped types when a family of properties follows a predictable rule, such as field errors, dirty flags, permission maps, or generated handler names. I avoid them when explicit fields are clearer.
Detailed Interview Answer
Mapped types let you transform object shapes.
Start with a model:
type Profile = {
name: string;
email: string;
age: number;
};
You can map over its keys.
type FieldErrors<T> = {
[Key in keyof T]?: string;
};
type ProfileErrors = FieldErrors<Profile>;
This creates:
type ProfileErrors = {
name?: string;
email?: string;
age?: string;
};
The derived type stays in sync with the original model.
keyof
keyof creates a union of property keys.
type ProfileKey = keyof Profile;
The result is:
"name" | "email" | "age"
Mapped types rely on this key union.
Indexed Access Types
You can access property types by key.
type EmailType = Profile["email"];
With generics:
function getValue<TObject, TKey extends keyof TObject>(
object: TObject,
key: TKey,
): TObject[TKey] {
return object[key];
}
The return type depends on the key.
Optional And Readonly Modifiers
Mapped types can add modifiers.
type ReadonlyFields<T> = {
readonly [Key in keyof T]: T[Key];
};
They can remove modifiers too.
type Mutable<T> = {
-readonly [Key in keyof T]: T[Key];
};
type Concrete<T> = {
[Key in keyof T]-?: T[Key];
};
The -? removes optionality.
The -readonly removes readonly.
Practical Form State
Mapped types are useful for forms.
type FormState<TValues> = {
values: TValues;
errors: { [Key in keyof TValues]?: string };
touched: { [Key in keyof TValues]?: boolean };
dirty: { [Key in keyof TValues]?: boolean };
};
Usage:
type LoginValues = {
email: string;
password: string;
};
type LoginFormState = FormState<LoginValues>;
When a field is added, the related state types follow.
Key Remapping
Key remapping uses as.
type ChangeHandlers<T> = {
[Key in keyof T as `on${Capitalize<string & Key>}Change`]: (value: T[Key]) => void;
};
For:
type Settings = {
theme: "light" | "dark";
density: "compact" | "comfortable";
};
You get:
type SettingsHandlers = {
onThemeChange: (value: "light" | "dark") => void;
onDensityChange: (value: "compact" | "comfortable") => void;
};
This is powerful, but it should be used sparingly.
Filtering Keys
You can map only selected keys.
type StringKeys<T> = {
[Key in keyof T]: T[Key] extends string ? Key : never;
}[keyof T];
For Profile, this produces:
"name" | "email"
Then use it:
type StringFieldErrors<T> = {
[Key in StringKeys<T>]?: string;
};
This pattern appears in table columns, filters, and form helpers.
Permission Maps
A realistic example:
type Feature = "handbook" | "practice" | "pdf" | "admin";
type PermissionMap = {
[Key in Feature]: {
read: boolean;
write: boolean;
};
};
This forces every feature to have a permission entry.
It avoids missing configuration.
Event Maps
Mapped types can keep event contracts consistent.
type EventPayloads = {
questionCompleted: { questionId: string; secondsSpent: number };
batchOpened: { batchSlug: string };
practiceRun: { language: "js" | "ts"; success: boolean };
};
type EventHandlers = {
[EventName in keyof EventPayloads]: (payload: EventPayloads[EventName]) => void;
};
Now each handler receives the right payload.
Common Mistakes
Common mistakes include:
using mapped types where explicit types are clearer
creating hard-to-read key remapping
forgetting symbol and number keys can exist
using string too broadly instead of keyof
creating derived types from unstable objects
confusing type transformation with runtime transformation
Mapped types should make repeated structure safer, not magical.
Senior Trade-Offs
Mapped types shine when:
keys must stay synchronized
configuration must be exhaustive
field-level state mirrors a value object
event payloads map to handlers
derived props follow a consistent naming rule
They are less useful when the result is only used once or the generated names surprise readers.
Interview Framing
In an interview, say:
Mapped types let me derive object shapes from a source model, which prevents drift. I use them for repeated field-level or configuration patterns, but I stop when key transformations become harder to understand than explicit types.
Revision Notes
- Mapped types iterate over key unions.
keyofand indexed access types are the foundation.- Modifiers can add or remove optional and readonly behavior.
- Key remapping can generate new property names.
- Mapped types are useful for forms, permissions, events, and config.
- Runtime data still needs runtime transformation.
Follow-Up Questions
- How does a mapped type work?
- What does
keyofproduce? - How do you remove optionality in a mapped type?
- When would key remapping be useful?
- Why can mapped types become hard to maintain?
How I Would Answer This In A Real Interview
I would explain that mapped types iterate across keys and create a new object type from an existing one. They are the foundation behind many built-in utilities. I use them when field state, permissions, event handlers, or config maps need to stay synchronized with a source model. I would be careful with key remapping because it is powerful, but explicit types can be easier for teams to maintain.