Hone logo
Hone
Problems

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:

  1. Input: The utility type should accept any valid TypeScript union type.
  2. Output: It must return an intersection type where each member of the original union becomes a constituent of the intersection.
  3. 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 null or undefined.

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.

Loading editor...
typescript