Transforming Union Types to Intersection Types in TypeScript
This challenge focuses on a fundamental concept in TypeScript's type system: type transformations. You will implement a utility type that takes a union of types and converts it into an intersection of those same types. This is a powerful technique for manipulating and refining types, often used in advanced TypeScript patterns.
Problem Description
Your task is to create a TypeScript utility type named UnionToIntersection<U> that accepts a union type U and returns an intersection type of all the types within that union.
Key Requirements:
- Input: The utility type should accept any valid TypeScript union type.
- Output: It must return an intersection type where each member of the original union becomes a constituent of the intersection.
- No External Libraries: The solution should be purely within TypeScript's type system.
Expected Behavior:
If you have a union like A | B | C, the UnionToIntersection<A | B | C> should result in A & B & C.
Edge Cases to Consider:
- Empty Union (though not directly representable, consider how types with no common members behave).
- Union of primitive types.
- Union of object types.
- Union containing
nullorundefined.
Examples
Example 1:
type Union1 = string | number | boolean;
type Intersection1 = UnionToIntersection<Union1>;
// Expected: Intersection1 should be string & number & boolean
Explanation: The union string | number | boolean is transformed into an intersection of these three primitive types.
Example 2:
interface TypeA { a: 1 }
interface TypeB { b: 2 }
interface TypeC { c: 3 }
type Union2 = TypeA | TypeB | TypeC;
type Intersection2 = UnionToIntersection<Union2>;
// Expected: Intersection2 should be TypeA & TypeB & TypeC
Explanation: The union of interfaces TypeA | TypeB | TypeC is converted into an intersection where an object must satisfy all three interfaces to be assignable to Intersection2.
Example 3:
type Union3 = { name: string } | { age: number };
type Intersection3 = UnionToIntersection<Union3>;
// Expected: Intersection3 should be { name: string } & { age: number }
Explanation: An object assignable to Intersection3 must have both a name property of type string and an age property of type number.
Constraints
- The solution must be implementable using only TypeScript's conditional types, infer keywords, and other built-in type manipulation features.
- The solution should be reasonably efficient for typical union sizes (up to a few dozen types).
Notes
This is a classic TypeScript type manipulation problem. The solution often involves leveraging how conditional types distribute over unions and how infer can be used in a recursive or iterative manner. Consider the behavior of conditional types when applied to a union – they often "distribute" over each member. You'll need to find a way to "collect" these distributed types into an intersection. A common pattern for this involves using infer in a way that forces the type to be resolved.