Bird
Raised Fist0
Typescriptprogramming~10 mins

Mapped type for deep transformations in Typescript - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Mapped type for deep transformations
Start with object type
↓
Check each property
↓
Is property a nested object?
No→Apply transformation to property
Yes↓
Recursively apply mapped type to nested object
↓
Combine transformed properties
↓
Return new transformed type
This flow shows how a mapped type checks each property of an object type, applies a transformation, and if the property is itself an object, applies the transformation recursively.
Execution Sample
Typescript
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
};
This code creates a type that makes all properties of an object and its nested objects readonly.
Execution Table
StepPropertyCheck if objectActionResulting Type
1nameNoApply readonlyreadonly name: string
2detailsYesRecursively apply DeepReadonlyreadonly details: DeepReadonly<{ age: number; address: { city: string; } }>
3details.ageNoApply readonlyreadonly age: number
4details.addressYesRecursively apply DeepReadonlyreadonly address: DeepReadonly<{ city: string; }>
5details.address.cityNoApply readonlyreadonly city: string
6End--Final DeepReadonly type created
7Exit--All properties processed
💡 All properties and nested properties have been processed and transformed to readonly.
Variable Tracker
VariableStartAfter 1After 2After 3After 4Final
T{ name: string; details: { age: number; address: { city: string; } } }{ name: string; details: { age: number; address: { city: string; } } }{ name: string; details: { age: number; address: { city: string; } } }{ name: string; details: { age: number; address: { city: string; } } }{ name: string; details: { age: number; address: { city: string; } } }DeepReadonly<{ name: string; details: { age: number; address: { city: string; } } } >
Key Moments - 2 Insights
Why do we check if a property is an object before applying the transformation?
Because only objects can have nested properties that need recursive transformation. Primitive types like string or number are transformed directly without recursion, as shown in steps 1 and 3 of the execution_table.
What happens if a property is an array or function?
In this example, arrays and functions are treated as objects, so the transformation recurses into them. This can cause unexpected behavior unless handled specifically. The execution_table assumes only plain objects for simplicity.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what is the resulting type for the property 'details.address.city' at step 5?
Areadonly city: string
Bcity: string
Creadonly address: string
Dreadonly city: number
💡 Hint
Check row 5 in the execution_table under 'Resulting Type' column.
At which step does the mapped type stop recursing deeper?
AStep 6
BStep 3
CStep 5
DStep 2
💡 Hint
Look for the last property checked that is not an object in the execution_table.
If the property 'details' was a string instead of an object, how would the action at step 2 change?
AIt would skip the property
BIt would apply readonly directly without recursion
CIt would recurse deeper anyway
DIt would cause a type error
💡 Hint
Refer to the condition 'Check if object' in execution_table step 2.
Concept Snapshot
Mapped type for deep transformations:
- Syntax: type NewType<T> = { [K in keyof T]: T[K] extends object ? NewType<T[K]> : Transform<T[K]> };
- Recursively applies transformation to nested objects
- Stops recursion on non-object types
- Useful for deep readonly, partial, or other modifications
- Remember to handle special cases like arrays or functions explicitly
Full Transcript
This visual execution shows how a mapped type in TypeScript can transform an object type deeply. Starting with an object type, each property is checked. If the property is an object, the mapped type applies itself recursively to transform nested properties. If not, it applies the transformation directly. The example uses DeepReadonly to make all properties readonly, including nested ones. The execution table traces each property step-by-step, showing when recursion happens and when it stops. Key moments clarify why the object check is important and what happens with arrays or functions. The quiz tests understanding of the recursion steps and property transformations. The snapshot summarizes the syntax and behavior for quick reference.

Practice

(1/5)
1.

What is the main purpose of a mapped type in TypeScript?

easy
A. To create a new type by transforming each property of an existing type
B. To execute functions asynchronously
C. To define runtime values for variables
D. To handle errors in promises

Solution

  1. Step 1: Understand mapped types

    Mapped types create new types by applying transformations to each property of an existing type.
  2. 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.
  3. Final Answer:

    To create a new type by transforming each property of an existing type -> Option A
  4. Quick Check:

    Mapped type = transform properties [OK]
Hint: Mapped types change keys' types one by one [OK]
Common Mistakes:
  • Confusing mapped types with runtime functions
  • Thinking mapped types handle async code
  • Mixing mapped types with error handling
2.

Which of the following is the correct syntax to define a simple mapped type that makes all properties of T optional?

type PartialType = ?
easy
A. type PartialType<T> = { [P in keyof T]: T[P] }
B. type PartialType<T> = { [P in keyof T]: Optional }
C. type PartialType<T> = { [P in keyof T]?: T[P] }
D. type PartialType<T> = { [P: string]: T }

Solution

  1. Step 1: Recall optional property syntax

    Adding a question mark after the property name makes it optional in mapped types.
  2. 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-existent Optional type.
  3. Final Answer:

    type PartialType<T> = { [P in keyof T]?: T[P] } -> Option C
  4. Quick Check:

    Optional properties use ? after key [OK]
Hint: Use ? after [P in keyof T] to make properties optional [OK]
Common Mistakes:
  • Forgetting the ? to mark properties optional
  • Using string index instead of keyof T
  • Assuming Optional<T> is built-in
3.

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;
  };
}
medium
A. { name: string; readonly address: { readonly city: string; readonly zip: number; } }
B. { name: string; address: { city: string; zip: number; } }
C. { readonly name: string; address: { city: string; zip: number; } }
D. { readonly name: string; readonly address: { readonly city: string; readonly zip: number; } }

Solution

  1. Step 1: Understand DeepReadonly mapped type

    It recursively applies readonly to all properties, including nested objects.
  2. Step 2: Apply to User interface

    Both name and address become readonly, and inside address, city and zip are also readonly.
  3. Final Answer:

    { readonly name: string; readonly address: { readonly city: string; readonly zip: number; } } -> Option D
  4. Quick Check:

    DeepReadonly = recursive readonly [OK]
Hint: DeepReadonly adds readonly recursively to all nested properties [OK]
Common Mistakes:
  • Forgetting readonly on nested properties
  • Assuming only top-level properties are readonly
  • Confusing shallow and deep readonly
4.

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];
}
medium
A. Incorrect use of semicolon instead of comma in mapped type
B. No error; the type is correct
C. The conditional type should check for primitives, not objects
D. Missing semicolon after the mapped property

Solution

  1. Step 1: Check mapped type syntax

    TypeScript object types, including mapped types, accept both commas and semicolons as separators, including trailing ones.
  2. 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.
  3. Final Answer:

    No error; the type is correct -> Option B
  4. Quick Check:

    Mapped types accept , or ; [OK]
Hint: Mapped types support , or ; separators [OK]
Common Mistakes:
  • Thinking semicolons are invalid in mapped types
  • Confusing separator rules from object literals
  • Overlooking recursive conditional logic
5.

How would you write a mapped type DeepNonNullable<T> that recursively removes null and undefined from all properties of T, including nested objects?

hard
A. type DeepNonNullable<T> = { [P in keyof T]: T[P] extends object ? DeepNonNullable<NonNullable<T[P]>> : NonNullable<T[P]> }
B. type DeepNonNullable<T> = { [P in keyof T]: NonNullable<T[P]> }
C. type DeepNonNullable<T> = { [P in keyof T]: T[P] extends object ? NonNullable<DeepNonNullable<T[P]>> : T[P] }
D. type DeepNonNullable<T> = { [P in keyof T]: T[P] extends object ? DeepNonNullable<T[P]> : T[P] }

Solution

  1. Step 1: Understand NonNullable and recursion

    We must remove null and undefined from each property, then recurse if the property is an object.
  2. 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.
  3. Final Answer:

    type DeepNonNullable<T> = { [P in keyof T]: T[P] extends object ? DeepNonNullable<NonNullable<T[P]>> : NonNullable<T[P]> } -> Option A
  4. Quick Check:

    Remove null then recurse for objects [OK]
Hint: Apply NonNullable before recursion for deep cleaning [OK]
Common Mistakes:
  • Not applying NonNullable before recursion
  • Recursing without removing null/undefined
  • Confusing order of conditional checks
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard