Hone logo
Hone
Problems

Implementing Distributive Types in TypeScript

TypeScript's powerful type system allows for elegant solutions to common programming challenges. Distributive types are a key feature that can significantly simplify how you handle unions of types. This challenge will guide you through understanding and implementing your own distributive types to transform union types in a controlled and predictable way.

Problem Description

Your task is to create a TypeScript utility type that takes a union of types and applies a transformation to each member of the union individually. This "distribution" is the core concept of distributive types. You need to design a generic type that accepts another type parameter representing the transformation to be applied. The utility type should then return a new union where each original type has been transformed.

Key Requirements:

  1. Generic Type: Create a generic type, let's call it Distribute<T, U>, where:
    • T represents the union of types to be distributed over.
    • U represents the type or transformation to be applied to each member of T.
  2. Distribution Logic: The Distribute type should intelligently apply the transformation U to each constituent type within the union T.
  3. Preserve Union Structure: The final output should be a union of the transformed types.

Expected Behavior:

If T is a union like A | B | C, and U is some transformation mechanism, Distribute<T, U> should result in U<A> | U<B> | U<C>.

Edge Cases:

  • What happens if T is not a union but a single type?
  • What happens if T is never?

Examples

Example 1: Mapping a union of primitive types to their string representations.

// Input:
type OriginalUnion = string | number | boolean;

// Expected Output (after applying a hypothetical stringifying transformation):
type StringifiedUnion = Distribute<OriginalUnion, typeof String>; // Or a custom stringify type

// For illustration, let's imagine Distribute<OriginalUnion, typeof String> would resolve to:
// string | string | string which simplifies to string
// This example might be misleading if not carefully defined. Let's refine the transformation.

Let's use a more concrete transformation for clarity.

Example 1 (Refined): Wrapping each type in a specific object.

// Input:
type MyUnion = "user" | "admin" | "guest";

// Define a transformation that wraps a type in a property called 'role'
type WrapInRole<T> = { role: T };

// Expected Output:
// Distribute<MyUnion, WrapInRole> should resolve to:
// { role: "user" } | { role: "admin" } | { role: "guest" }

Example 2: Applying a conditional type transformation.

// Input:
type DataStatus = "loading" | "success" | "error" | undefined;

// Define a transformation that maps statuses to their data structures (or null for undefined)
type MapStatusToData<T> = T extends "success"
  ? { data: any }
  : T extends "error"
  ? { error: string }
  : T extends "loading"
  ? {}
  : null;

// Expected Output:
// Distribute<DataStatus, MapStatusToData> should resolve to:
// {} | { data: any } | { error: string } | null

Example 3: Handling a non-union type.

// Input:
type SingleType = number;

// Using the same WrapInRole transformation from Example 1:
// Distribute<SingleType, WrapInRole> should resolve to:
// { role: number }

Constraints

  • The Distribute type must be implemented using TypeScript's built-in type system features (generics, conditional types, etc.).
  • The solution should be a single generic type definition.
  • No runtime code is required; this is purely a type-level challenge.
  • The solution should be efficient and not lead to excessive type computation or recursion depth.

Notes

The key to implementing distributive types lies in how TypeScript handles generic conditional types when the type parameter being distributed over is a union. Pay close attention to how T extends ... ? ... : ... behaves when T is a union. Consider creating a simple, focused transformation function (like WrapInRole or MapStatusToData) to test your Distribute type. Think about how never should be handled in your distribution.

Loading editor...
typescript