0
0
Typescriptprogramming~15 mins

Mapped type with conditional types in Typescript - Deep Dive

Choose your learning style9 modes available
Overview - Mapped type with conditional types
What is it?
Mapped types with conditional types in TypeScript let you create new object types by transforming each property of an existing type based on a condition. This means you can change the type of each property depending on some rule, like whether it extends another type. It helps you write flexible and reusable code that adapts types automatically.
Why it matters
Without mapped types combined with conditional types, you would have to write many similar types manually for different cases, which is slow and error-prone. This feature saves time and reduces bugs by automating type transformations. It makes your code smarter and easier to maintain, especially in large projects where types change often.
Where it fits
Before learning this, you should understand basic TypeScript types, interfaces, and simple mapped types. After mastering this, you can explore advanced utility types, type inference, and generic programming in TypeScript.
Mental Model
Core Idea
Mapped types with conditional types transform each property of a type by applying a rule that decides its new type based on a condition.
Think of it like...
Imagine you have a box of different fruits, and you want to pack each fruit differently depending on whether it's a citrus or not. You check each fruit and decide the wrapping based on its type. Similarly, mapped types check each property and wrap it with a new type based on a condition.
Type T = {
  [Key in keyof OriginalType]: OriginalType[Key] extends Condition ? TrueType : FalseType
}

Where:
- OriginalType: the starting object type
- Key: each property name
- Condition: the test applied to each property's type
- TrueType: new type if condition passes
- FalseType: new type if condition fails
Build-Up - 7 Steps
1
FoundationUnderstanding basic mapped types
🤔
Concept: Mapped types create new types by looping over properties of an existing type.
In TypeScript, you can create a new type by taking all keys from an existing type and changing their types. For example: ```typescript type Person = { name: string; age: number }; type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] }; ``` Here, ReadonlyPerson makes all properties readonly.
Result
ReadonlyPerson has the same keys as Person but all properties are readonly.
Understanding that mapped types loop over keys lets you see how to systematically change types property by property.
2
FoundationBasics of conditional types
🤔
Concept: Conditional types choose one type or another based on a condition.
A conditional type looks like this: ```typescript type IsString = T extends string ? 'yes' : 'no'; ``` If T is string, the type is 'yes', otherwise 'no'. For example, IsString is 'no'.
Result
Conditional types let you write rules that pick types based on conditions.
Knowing conditional types lets you create flexible type rules that adapt based on input types.
3
IntermediateCombining mapped and conditional types
🤔Before reading on: do you think conditional types can be used inside mapped types to change property types? Commit to yes or no.
Concept: You can use conditional types inside mapped types to transform each property differently based on its type.
For example, to make all string properties optional but keep others required: ```typescript type OptionalStrings = { [K in keyof T]: T[K] extends string ? T[K] | undefined : T[K]; }; // Usage: type Example = { a: string; b: number }; type Result = OptionalStrings; // Result is { a: string | undefined; b: number } ```
Result
Each property is checked: if it's string, it becomes optional; otherwise, it stays the same.
Using conditional types inside mapped types unlocks powerful, property-level type transformations.
4
IntermediateFiltering properties with conditional types
🤔Before reading on: can conditional types inside mapped types remove properties? Commit to yes or no.
Concept: You can filter properties by mapping some to never, which removes them from the resulting type.
Example: keep only string properties: ```typescript type OnlyStrings = { [K in keyof T]: T[K] extends string ? T[K] : never; }; // Usage: type Example = { a: string; b: number; c: string }; type Result = OnlyStrings; // Result is { a: string; b: never; c: string } ``` To remove never properties, use a helper like Pick or a mapped type with key remapping (TypeScript 4.1+).
Result
Properties not matching the condition become never, which can be removed to filter keys.
Mapping to never is a key trick to filter properties by type.
5
IntermediateKey remapping with conditional types
🤔
Concept: TypeScript 4.1+ lets you rename or remove keys in mapped types using conditional types.
You can write: ```typescript type FilterStrings = { [K in keyof T as T[K] extends string ? K : never]: T[K]; }; // Usage: type Example = { a: string; b: number; c: string }; type Result = FilterStrings; // Result is { a: string; c: string } ``` Here, keys for non-string properties become never and are removed.
Result
The resulting type only has keys whose values are strings.
Key remapping combined with conditional types enables precise control over which properties appear in the new type.
6
AdvancedUsing distributive conditional types in mapped types
🤔Before reading on: do you think conditional types distribute over unions inside mapped types? Commit to yes or no.
Concept: Conditional types distribute over union types, affecting how mapped types transform properties with union types.
For example: ```typescript type ToArray = T extends any ? T[] : never; type Result = ToArray; // Result is string[] | number[] ``` When used in mapped types, this means each union member is transformed separately.
Result
Mapped types with conditional types can produce union types by distributing over unions.
Understanding distributive conditional types helps predict complex type transformations involving unions.
7
ExpertPerformance and pitfalls of complex mapped conditional types
🤔Before reading on: do you think deeply nested mapped conditional types can slow down TypeScript's compiler? Commit to yes or no.
Concept: Complex mapped types with conditional logic can cause slow compilation and confusing error messages.
When you nest many conditional and mapped types, TypeScript's type checker does more work, which can slow down your editor and build. Also, error messages may become hard to understand. To avoid this, simplify types, split them into smaller parts, or use helper types.
Result
Overusing mapped conditional types can hurt developer experience and performance.
Knowing the limits of mapped conditional types helps write maintainable and performant TypeScript code.
Under the Hood
TypeScript's compiler processes mapped types by iterating over each property key of the input type. For each key, it applies the conditional type check to decide the property's new type. Conditional types are evaluated by checking if the property type extends the condition type, distributing over unions if present. Key remapping uses special syntax to rename or remove keys by mapping them to 'never'. The compiler builds the resulting type by combining all transformed properties.
Why designed this way?
Mapped types and conditional types were introduced to make TypeScript's type system more expressive and reusable. Before them, developers had to write many similar types manually. The design balances power and readability, allowing fine-grained control over types while keeping syntax concise. Key remapping was added later to enable filtering and renaming, addressing common use cases that earlier versions couldn't handle.
Original Type
  └─ keys: K1, K2, K3
      ├─ Property K1: Type1
      │     └─ Check: Type1 extends Condition?
      │           ├─ Yes → NewType1
      │           └─ No → OtherType1
      ├─ Property K2: Type2
      │     └─ Check: Type2 extends Condition?
      │           ├─ Yes → NewType2
      │           └─ No → OtherType2
      └─ Property K3: Type3
            └─ Check: Type3 extends Condition?
                  ├─ Yes → NewType3
                  └─ No → OtherType3

Resulting Type
  └─ keys: possibly renamed or removed
      ├─ Property K1: NewType1 or OtherType1
      ├─ Property K2: NewType2 or OtherType2
      └─ Property K3: NewType3 or OtherType3
Myth Busters - 3 Common Misconceptions
Quick: Does mapping a property to 'never' keep it in the resulting type? Commit to yes or no.
Common Belief:Mapping a property to 'never' keeps the property with type 'never' in the new type.
Tap to reveal reality
Reality:When using key remapping, properties mapped to 'never' are removed from the resulting type entirely.
Why it matters:Believing 'never' properties stay causes confusion and bugs when expected keys disappear or remain unexpectedly.
Quick: Do conditional types inside mapped types always apply to the whole object at once? Commit to yes or no.
Common Belief:Conditional types inside mapped types apply once to the entire object type.
Tap to reveal reality
Reality:Conditional types apply separately to each property type during mapping, not to the whole object at once.
Why it matters:
Quick: Can mapped types with conditional types cause slow compilation? Commit to yes or no.
Common Belief:Mapped types with conditional types are always fast and safe to use without performance concerns.
Tap to reveal reality
Reality:Complex nested mapped conditional types can slow down TypeScript's compiler and cause confusing errors.
Why it matters:Ignoring performance impact can degrade developer experience and slow down builds in large projects.
Expert Zone
1
Conditional types distribute over unions, which can cause unexpected union expansions inside mapped types.
2
Key remapping with 'never' removes properties, but mapping to 'undefined' or 'null' does not remove keys, only changes their types.
3
Excessive nesting of mapped and conditional types can cause exponential type checking time, so splitting types improves performance.
When NOT to use
Avoid using mapped types with complex conditional logic when simple utility types or explicit interfaces suffice. For very large types or performance-critical code, prefer simpler types or runtime checks. Also, do not use mapped conditional types to manipulate deeply nested objects without helper types, as it becomes hard to maintain.
Production Patterns
In real-world projects, mapped types with conditional types are used to create flexible API response types, transform data shapes for UI components, and build reusable validation schemas. They enable writing generic libraries that adapt to different data models without rewriting types.
Connections
Generic programming
Mapped types with conditional types build on generics by adding conditional logic to generic type parameters.
Understanding generics helps grasp how mapped conditional types adapt types based on input, making code reusable and safe.
Functional programming
Mapped types with conditional types resemble functional map and filter operations applied to types instead of values.
Seeing type transformations as functional operations clarifies how types flow and change systematically.
Set theory (mathematics)
Conditional types act like set membership tests, deciding if a type belongs to a subset to choose a new type.
Recognizing conditional types as set filters helps understand type narrowing and union distribution.
Common Pitfalls
#1Trying to filter properties by mapping them to 'never' without key remapping.
Wrong approach:type Filtered = { [K in keyof T]: T[K] extends string ? T[K] : never; };
Correct approach:type Filtered = { [K in keyof T as T[K] extends string ? K : never]: T[K]; };
Root cause:Mapping to 'never' alone does not remove keys; key remapping syntax is required to exclude properties.
#2Assuming conditional types inside mapped types apply to the whole object instead of each property.
Wrong approach:type Wrong = T extends string ? string : number; // applied to whole T, not per property
Correct approach:type Correct = { [K in keyof T]: T[K] extends string ? string : number }; // per property
Root cause:Misunderstanding the scope of conditional types leads to incorrect type transformations.
#3Writing deeply nested mapped conditional types without considering performance.
Wrong approach:type Complex = { [K in keyof T]: T[K] extends object ? { [P in keyof T[K]]: T[K][P] extends string ? string : number } : T[K] };
Correct approach:Split complex types into smaller parts or use helper types to avoid deep nesting and improve compiler speed.
Root cause:Not realizing that complex nested types increase compiler workload and slow down development.
Key Takeaways
Mapped types with conditional types let you transform each property of a type based on rules, making types flexible and reusable.
Conditional types inside mapped types apply separately to each property, enabling fine-grained control over type transformations.
Key remapping with 'never' allows filtering properties by removing unwanted keys from the resulting type.
Distributive conditional types expand over unions, which can create complex union types inside mapped types.
Complex nested mapped conditional types can slow down TypeScript's compiler, so use them thoughtfully and split complex types.