Hone logo
Hone
Problems

Union to Intersection in TypeScript

This challenge explores a powerful technique in TypeScript: transforming unions into intersections. Understanding how to manipulate types like this is crucial for writing flexible and maintainable code, especially when dealing with complex type systems and generics. You'll be tasked with creating a function that takes a union type and returns its intersection.

Problem Description

You need to implement a function called unionToIntersection that accepts a union type as input and returns its intersection type. The intersection type combines all the properties of the types within the union, resulting in a new type that only includes properties present in all of the original types. The function should be type-safe and correctly handle various union types, including those with overlapping and distinct properties.

Key Requirements:

  • The function must be generic, accepting any union type U.
  • The return type must be the intersection of U.
  • The function should not perform any runtime operations; it should solely operate on the type level.
  • The function should be correctly typed to ensure type safety.

Expected Behavior:

Given a union type, the function should return a new type that represents the intersection of all types within the union. This means the resulting type will only contain properties that are present in every type within the union.

Edge Cases to Consider:

  • Empty Union: If the input union is empty (e.g., never), the intersection should also be never.
  • Single Type Union: If the input union contains only one type, the intersection should be that type itself.
  • Overlapping Properties: If types within the union have properties with the same name but different types, the resulting intersection type should use the most restrictive (narrowest) type for that property.
  • Distinct Properties: If types within the union have distinct properties, the intersection type should include all of those properties.

Examples

Example 1:

type A = { a: string; b: number };
type B = { b: number; c: boolean };
type C = { b: number };

type Result1 = unionToIntersection<A | B | C>; // Expected: { b: number }

Explanation: Types A, B, and C all have the property b: number. Therefore, the intersection is { b: number }.

Example 2:

type D = { x: string; y: number };
type E = { x: number; z: boolean };

type Result2 = unionToIntersection<D | E>; // Expected: {}

Explanation: Types D and E have no properties in common. Therefore, the intersection is an empty object {}.

Example 3:

type F = { a: string; b: number };
type G = { a: string; b: number };

type Result3 = unionToIntersection<F | G>; // Expected: { a: string; b: number }

Explanation: Types F and G are identical. The intersection is the type itself.

Example 4:

type H = { a: string };
type I = never;

type Result4 = unionToIntersection<H | I>; // Expected: never

Explanation: One type is never. The intersection is never.

Constraints

  • The input union type U can contain any number of types (including zero).
  • The types within the union can have any valid TypeScript properties.
  • The function must be implemented using TypeScript's type system features (no runtime operations).
  • The solution should be reasonably efficient in terms of type inference. While performance at runtime isn't a concern, excessively complex type manipulations can impact compilation time.

Notes

This problem requires a good understanding of TypeScript's conditional types and type inference. Consider using a recursive approach to iterate through the union types. The key is to leverage TypeScript's ability to perform type calculations at compile time. Think about how you can use conditional types to check if a property exists in all types within the union.

Loading editor...
typescript