Hone logo
Hone
Problems

Type-Level Combinators in TypeScript

Type-level programming allows us to perform computations and manipulations on types themselves, rather than values. This is incredibly useful for creating highly generic and reusable code, enforcing constraints at compile time, and generating types based on other types. This challenge asks you to implement a few fundamental type-level combinators in TypeScript, demonstrating your understanding of conditional types, mapped types, and inference.

Problem Description

You are tasked with implementing several type-level combinators. These combinators are functions that operate on types and produce new types. Specifically, you need to implement UnionToIntersection, Diff, and Filter. These combinators are building blocks for more complex type manipulations.

  • UnionToIntersection<T>: This combinator takes a union type T and transforms it into an intersection type. This is useful because unions represent "or" relationships, while intersections represent "and" relationships. Combining them allows you to extract common properties from a union.
  • Diff<T, U>: This combinator takes two types, T and U, and produces a new type that contains all the properties of T that are not present in U. Essentially, it removes properties from T that exist in U.
  • Filter<T, Condition>: This combinator takes a type T and a Condition type (which is a conditional type). It produces a new type that only includes properties from T that satisfy the Condition.

Key Requirements:

  • All combinators must be implemented using TypeScript's type system features (conditional types, mapped types, etc.).
  • The implementations should be generic and work with various types.
  • The implementations should be type-safe and avoid runtime errors.

Expected Behavior:

The type definitions for the combinators should be accurate and produce the expected results when used in type contexts. The examples below illustrate the expected behavior.

Examples

Example 1: UnionToIntersection

type T1 = { a: string; b: number };
type T2 = { b: boolean; c: Date };
type T3 = UnionToIntersection<T1 | T2>; // Expected: { a: string; b: number; c: Date }

Explanation: T1 | T2 is a union of two types. UnionToIntersection<T1 | T2> should result in an intersection containing all properties from both T1 and T2.

Example 2: Diff

type T1 = { a: string; b: number; c: boolean };
type T2 = { b: number; d: Date };
type T3 = Diff<T1, T2>; // Expected: { a: string; c: boolean }

Explanation: Diff<T1, T2> should remove the b property from T1 because it exists in T2. The d property in T2 is ignored because it's not in T1.

Example 3: Filter

type T = { a: string; b: number; c: boolean; d: undefined };
type Condition = <K extends string>(key: K) => key extends 'a' ? true : key extends 'b' ? false : key extends 'c' ? true : false;

type Filtered = Filter<T, Condition>; // Expected: { a: string; c: boolean }

Explanation: Filter<T, Condition> should only include properties a and c from T because the Condition returns true for those keys.

Constraints

  • Type Safety: The implementations must be type-safe and avoid any type errors.
  • TypeScript Version: The code should be compatible with TypeScript 4.0 or higher.
  • No Runtime Code: The solution should consist only of type definitions. No runtime code is allowed.
  • Readability: The code should be well-formatted and easy to understand.

Notes

  • Consider using mapped types and conditional types extensively to achieve the desired behavior.
  • The Diff combinator can be implemented using Exclude and mapped types.
  • The Filter combinator requires a conditional type that checks each property against the provided Condition.
  • The Condition type in the Filter example is a simplified representation. In a real-world scenario, the condition might be more complex. The key is to understand how to use a conditional type to filter properties based on a given criteria.
  • Think about how to handle optional properties correctly in the Diff and Filter combinators. They should behave as expected when dealing with optional properties.
Loading editor...
typescript