Mapped types let you change the shape of an object type. Deep transformations help you change nested objects step-by-step.
Mapped type for deep transformations in Typescript
type DeepTransform<T> = {
[K in keyof T]: T[K] extends object ? DeepTransform<T[K]> : /* transformation */ T[K]
}The syntax uses [K in keyof T] to loop over keys.
Use conditional types to check if a property is an object and apply recursion.
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
};type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
};type DeepStringToNumber<T> = {
[K in keyof T]: T[K] extends string ? number : T[K] extends object ? DeepStringToNumber<T[K]> : T[K]
};This program creates a deeply readonly User type. Trying to change any property causes an error. The console logs show the values.
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
};
interface User {
name: string;
address: {
street: string;
city: string;
};
}
const user: DeepReadonly<User> = {
name: "Alice",
address: {
street: "123 Main St",
city: "Wonderland"
}
};
// user.name = "Bob"; // Error: cannot assign to readonly property
// user.address.city = "Elsewhere"; // Error: cannot assign to readonly property
console.log(user.name);
console.log(user.address.city);Deep transformations use recursion in types to handle nested objects.
Be careful with types like arrays or functions; you might need extra checks.
Mapped types do not change runtime behavior, only type checking.
Mapped types let you change object types key by key.
Deep transformations apply changes to nested objects recursively.
Use conditional types to decide when to recurse or transform.
Practice
What is the main purpose of a mapped type in TypeScript?
Solution
Step 1: Understand mapped types
Mapped types create new types by applying transformations to each property of an existing type.Step 2: Compare options
Only To create a new type by transforming each property of an existing type describes this behavior correctly; others relate to runtime or async concepts.Final Answer:
To create a new type by transforming each property of an existing type -> Option AQuick Check:
Mapped type = transform properties [OK]
- Confusing mapped types with runtime functions
- Thinking mapped types handle async code
- Mixing mapped types with error handling
Which of the following is the correct syntax to define a simple mapped type that makes all properties of T optional?
type PartialType = ?Solution
Step 1: Recall optional property syntax
Adding a question mark after the property name makes it optional in mapped types.Step 2: Analyze options
type PartialType<T> = { [P in keyof T]?: T[P] } uses?:correctly to make properties optional. type PartialType<T> = { [P in keyof T]: T[P] } keeps properties required. type PartialType<T> = { [P: string]: T } uses string keys incorrectly. type PartialType<T> = { [P in keyof T]: Optional } uses a non-existentOptionaltype.Final Answer:
type PartialType<T> = { [P in keyof T]?: T[P] } -> Option CQuick Check:
Optional properties use ? after key [OK]
- Forgetting the ? to mark properties optional
- Using string index instead of keyof T
- Assuming Optional<T> is built-in
Given the following code, what is the type of DeepReadonly<User>?
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
};
interface User {
name: string;
address: {
city: string;
zip: number;
};
}Solution
Step 1: Understand DeepReadonly mapped type
It recursively appliesreadonlyto all properties, including nested objects.Step 2: Apply to User interface
Bothnameandaddressbecome readonly, and insideaddress,cityandzipare also readonly.Final Answer:
{ readonly name: string; readonly address: { readonly city: string; readonly zip: number; } } -> Option DQuick Check:
DeepReadonly = recursive readonly [OK]
- Forgetting readonly on nested properties
- Assuming only top-level properties are readonly
- Confusing shallow and deep readonly
Identify the error in this deep transformation mapped type:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
}Solution
Step 1: Check mapped type syntax
TypeScript object types, including mapped types, accept both commas and semicolons as separators, including trailing ones.Step 2: Analyze the code
The syntax is valid, and the logic correctly makes properties optional while recursing DeepPartial on nested objects and preserving primitives.Final Answer:
No error; the type is correct -> Option BQuick Check:
Mapped types accept , or ; [OK]
- Thinking semicolons are invalid in mapped types
- Confusing separator rules from object literals
- Overlooking recursive conditional logic
How would you write a mapped type DeepNonNullable<T> that recursively removes null and undefined from all properties of T, including nested objects?
Solution
Step 1: Understand NonNullable and recursion
We must remove null and undefined from each property, then recurse if the property is an object.Step 2: Analyze options
type DeepNonNullable<T> = { [P in keyof T]: T[P] extends object ? DeepNonNullable<NonNullable<T[P]>> : NonNullable<T[P]> } first applies NonNullable to the property, then recursively applies DeepNonNullable to the cleaned type. This ensures null/undefined are removed at every level.Final Answer:
type DeepNonNullable<T> = { [P in keyof T]: T[P] extends object ? DeepNonNullable<NonNullable<T[P]>> : NonNullable<T[P]> } -> Option AQuick Check:
Remove null then recurse for objects [OK]
- Not applying NonNullable before recursion
- Recursing without removing null/undefined
- Confusing order of conditional checks
Ready to prove it?
Pass the test and this pattern moves unknown
