interface Person { name: string; age: number; }
type Stringify<T> = { [K in keyof T]: string };
type StringPerson = Stringify<Person>;
// { name: string; age: string }
Topics
26
Classes: Access Modifiers, Abstract Classes & Implements
Conditional Types
Declaration Files
Decorators
Discriminated Unions & Exhaustiveness Checking
Enums
Function Types, Overloads & Optional/Default Parameters
Generics
Index Signatures & Record Types
Mapped Types
Migrating JavaScript to TypeScript
Modules
Namespaces & Declaration Merging
Never, Unknown & Void Types
Nullability: strictNullChecks, Optional Chaining & Nullish Coalescing
Primitive Types, Arrays & Tuples
Readonly, const Assertions & Immutability
Structural Typing & Duck Typing
Template Literal Types
tsconfig & Compiler Options
Type Assertions & Type Casting
Type Inference & Contextual Typing
Type Narrowing & Guards
Types & Interfaces
Union & Intersection Types
Utility Types
Mapped Types
10 questions found
A mapped type generates a new object type by iterating over the keys of an existing type T (via `keyof T`) and applying a transformation to each property's type, letting you derive new types from existing ones without manually re-listing every property.
Real-world example
Deriving a form-values type where every field of a data model is represented as a string (matching raw form input).
Index Signatures & Record Types
Partial<T> is implemented as `{ [K in keyof T]?: T[K] }` — it maps over every key of T and adds a '?' to make each property optional, producing a version of T where every field can be omitted.
interface Person { name: string; age: number; }
type PartialPerson = Partial<Person>;
// { name?: string; age?: number }
function updatePerson(updates: PartialPerson) { /* ... */ }
Real-world example
Typing an update/patch function's parameter where callers only need to supply the fields they want to change.
Never
Unknown & Void Types
By default, a mapped type COPIES existing modifiers ('?' and readonly) from the source type; explicitly writing `-?` or `-readonly` REMOVES that modifier from every mapped property, while `+?` or `+readonly` (equivalent to just writing them plainly) explicitly ADDS it — this is how built-in Required<T> and Writable-style utilities are implemented.
type Required<T> = { [K in keyof T]-?: T[K] }; // removes optionality
type Mutable<T> = { [K in keyof T]-readonly: T[K] }; // removes readonly
interface Config { readonly name?: string; }
type FullyMutableRequired = Mutable<Required<Config>>;
// { name: string }
Real-world example
Building custom utility types that strip readonly or optionality from a type, mirroring built-in Required<T>.
Readonly
const Assertions & Immutability
Adding `as NewKeyExpression` after the `K in keyof T` clause lets you transform the resulting property NAME (not just its value type) — commonly combined with template literal types to systematically rename every key, like generating getter/setter method names from data properties.
type EventHandlers<T> = {
[K in keyof T as `on${Capitalize<string & K>}Change`]: (value: T[K]) => void
};
interface FormFields { name: string; age: number; }
type Handlers = EventHandlers<FormFields>;
// { onNameChange: (value: string) => void; onAgeChange: (value: number) => void }
Real-world example
Auto-generating a consistent set of event-handler prop names from a data model, avoiding manual duplication and drift.
Template Literal Types
How would you use a mapped type's 'as never' technique to filter out specific keys from a type based on their value type?
IntermediateCombine key remapping with a conditional expression in the 'as' clause: for keys you want to KEEP, map to their normal name; for keys you want to EXCLUDE, map to 'never' — TypeScript automatically drops any property mapped to a 'never' key from the resulting type.
type OmitFunctions<T> = {
[K in keyof T as T[K] extends Function ? never : K]: T[K]
};
interface Model { name: string; age: number; save(): void; }
type DataOnly = OmitFunctions<Model>;
// { name: string; age: number } -- 'save' is filtered out
Real-world example
Deriving a plain data-only type from a class instance type, excluding its methods automatically.
Structural Typing & Duck Typing
How would you implement a deep 'DeepReadonly<T>' mapped type that recursively makes every nested property readonly?
AdvancedCombine a mapped type with a conditional type: for each key, check if its value is itself an object (and not a function or array edge case you want to treat specially); if so, recurse DeepReadonly into it, otherwise leave the primitive value type as-is — applied alongside the readonly modifier at every level.
type DeepReadonly<T> = T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
interface Config { server: { host: string; port: number }; }
type FrozenConfig = DeepReadonly<Config>;
// server itself and server.host/server.port are all readonly
Real-world example
Typing a fully immutable application state tree or configuration object, preventing accidental deep mutation anywhere.
Readonly
const Assertions & Immutability
How do homomorphic mapped types (those that map directly over `keyof T` for a generic T) preserve extra information like optionality, compared to non-homomorphic ones?
AdvancedA homomorphic mapped type — one written as `{ [K in keyof T]: ... }` directly over a generic T — automatically preserves T's original modifiers (optional, readonly) and array/tuple structure unless explicitly overridden with +/-. A non-homomorphic mapped type (iterating over a fixed union of string literals instead of `keyof T`) does NOT get this automatic preservation, since there's no single source type to copy modifiers from.
// Homomorphic: preserves Person's original optional/readonly modifiers automatically
type Copy<T> = { [K in keyof T]: T[K] };
// Non-homomorphic: modifiers aren't inherited from any single source type
type FixedKeys = { [K in 'a' | 'b']: string };
Real-world example
Understanding why some custom mapped-type utilities unexpectedly preserve readonly/optional modifiers while others don't.
Never
Unknown & Void Types
How would you write a mapped type that converts every method on an interface into a Promise-returning async version, for a mock or proxy implementation?
AdvancedUse a conditional type inside the mapped type to detect function-typed properties specifically, and for those, produce a new function type with the same parameters but a Promise-wrapped return type — leaving non-function properties unchanged (or omitted, depending on the use case).
type Asyncify<T> = {
[K in keyof T]: T[K] extends (...args: infer A) => infer R
? (...args: A) => Promise<R>
: T[K]
};
interface Api { getUser(id: string): User; }
type AsyncApi = Asyncify<Api>;
// { getUser(id: string): Promise<User> }
Real-world example
Generating a fully-typed async mock or remote-proxy version of a synchronous service interface automatically.
Function Types
Overloads & Optional/Default Parameters
`keyof (A | B)` produces only the INTERSECTION of keys common to both A and B (since only guaranteed-present keys are safe to access on either type), which can be a surprising and non-obvious result if you expected all keys from both types combined.
interface A { x: number; y: number; }
interface B { x: number; z: number; }
type Keys = keyof (A | B); // 'x' only — the only key guaranteed on both
Real-world example
Debugging an unexpectedly narrow mapped type result, tracing it back to keyof behavior on a union rather than an intersection.
Union & Intersection Types
How would you build a type-safe 'Builder' pattern using a mapped type that tracks which properties have already been set?
AdvancedModel the builder's state as a generic type parameter representing the set of already-set keys; each 'with' method returns a new builder type with that key added to the state (via a union or intersection update), and a 'build()' method is only made available (via a conditional type) once all required keys are present in the tracked state.
type Builder<T, Set extends keyof T = never> = {
[K in keyof T]: (value: T[K]) => Builder<T, Set | K>
} & (Set extends keyof T ? { build(): T } : {});
// (Simplified illustration -- full implementation requires more advanced conditional plumbing)
Real-world example
Designing a fluent configuration builder API that only allows calling .build() once every required field has actually been set, enforced at compile time.
Conditional Types