Mapped type with conditional types in Typescript - Time & Space Complexity
We want to understand how the time needed to create a new type changes as the input type grows.
How does the process of mapping and checking conditions for each property scale?
Analyze the time complexity of the following code snippet.
type FilterStrings<T> = {
[K in keyof T]: T[K] extends string ? K : never
}[keyof T];
// This creates a union of keys whose values are strings
This code creates a new type by checking each property of T and keeping only those keys whose values are strings.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Iterating over each key in the input type T.
- How many times: Once for every property key in T.
As the number of properties in T grows, the number of checks grows the same way.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 checks |
| 100 | 100 checks |
| 1000 | 1000 checks |
Pattern observation: The work grows directly with the number of properties, one check per property.
Time Complexity: O(n)
This means the time to create the mapped type grows linearly with the number of properties in the input type.
[X] Wrong: "The conditional check inside the mapped type runs multiple times per property or grows faster than the number of properties."
[OK] Correct: Each property is checked exactly once, so the total work grows only with the number of properties, not more.
Understanding how mapped types with conditions scale helps you reason about type transformations and their cost, a useful skill for writing efficient and maintainable TypeScript code.
"What if we nested another mapped type inside this one? How would the time complexity change?"
Practice
type Example<T> = { [K in keyof T]: T[K] extends string ? number : boolean }Solution
Step 1: Understand mapped types
Mapped types loop over each key in the original type to create a new type with transformed properties.Step 2: Apply conditional types
The conditional type checks if the property type extends string; if yes, it changes to number, else to boolean.Final Answer:
It creates a new type where string properties become number and others become boolean. -> Option AQuick Check:
Mapped + conditional changes types based on condition [OK]
- Thinking it removes properties instead of transforming them
- Assuming all types become string
- Confusing conditional type logic
Solution
Step 1: Check mapped type syntax
The correct syntax uses square brackets with 'in' keyword: [K in keyof T].Step 2: Verify conditional type usage
The conditional type is written as T[K] extends number ? string : boolean, which is correct.Final Answer:
type NewType<T> = { [K in keyof T]: T[K] extends number ? string : boolean } -> Option BQuick Check:
Mapped type syntax = [K in keyof T] [OK]
- Using 'K in T' instead of 'K in keyof T'
- Using 'K of keyof T' which is invalid
- Missing square brackets around K in mapped type
type Flags = { a: string; b: number; c: boolean };
type Result = { [K in keyof Flags]: Flags[K] extends number ? 'num' : 'not-num' };
const test: Result = { a: 'not-num', b: 'num', c: 'not-num' };What is the type of
test.b?Solution
Step 1: Analyze the mapped type Result
For each key in Flags, if the property type is number, Result's property is 'num', else 'not-num'.Step 2: Check property b in Flags
b is number, so Result['b'] is 'num'.Final Answer:
'num' -> Option CQuick Check:
Flags['b'] is number, so Result['b'] = 'num' [OK]
- Confusing original property type with mapped type
- Assuming test.b is number instead of 'num'
- Ignoring conditional type logic
type Fix<T> = { [P in keyof T]: T[P] extends string ? number : }Solution
Step 1: Check conditional type syntax
Conditional types require both true and false branches separated by colon.Step 2: Identify missing false branch
The code ends with ':' but no type after it, causing syntax error.Final Answer:
Missing type after colon in conditional type -> Option DQuick Check:
Conditional types need both branches [OK]
- Leaving out false branch in conditional type
- Misplacing square brackets in mapped type
- Thinking keyof is wrong here
Choose the correct mapped type:
Solution
Step 1: Understand key remapping in mapped types
Using 'as' allows filtering keys by condition, replacing keys with 'never' to omit them.Step 2: Check each option
type FilterStrings<T> = { [K in keyof T as T[K] extends string ? K : never]: T[K] } correctly uses 'as' with conditional to keep keys where T[K] extends string; others become never (excluded).Final Answer:
type FilterStrings<T> = { [K in keyof T as T[K] extends string ? K : never]: T[K] } -> Option AQuick Check:
Key remapping with 'as' filters keys [OK]
- Not using 'as' for key filtering
- Returning never as value instead of key
- Confusing key and value conditions
Ready to prove it?
Pass the test and this pattern moves unknown
