Hone logo
Hone
Problems

Type-Level Pattern Matching with Conditional Types

Type-level programming in TypeScript allows us to perform computations and logic at compile time, enabling powerful type transformations and safer code. This challenge focuses on implementing basic pattern matching capabilities using conditional types, mimicking the behavior of pattern matching found in functional programming languages. Successfully completing this challenge will demonstrate a strong understanding of TypeScript's advanced type system and its potential for compile-time logic.

Problem Description

You are tasked with creating a type-level function called Match that performs pattern matching on a type. The Match function will take two arguments: a subject type (the type to be matched) and a patterns type (an array of type patterns to match against). Each pattern in the patterns array will be a tuple of [pattern, result] where pattern is a type to match and result is the type to return if the subject matches the pattern.

The Match function should return the type that corresponds to the first matching pattern. If no patterns match the subject, it should return never. The matching is based on structural equality – meaning two types match if they have the same structure and properties, regardless of their names.

Key Requirements:

  • The Match function must be implemented using conditional types and type inference.
  • The function must handle the case where no patterns match the subject type.
  • The function should be generic to work with any type as the subject.
  • The patterns type must be an array of tuples, where each tuple contains a pattern type and a result type.

Expected Behavior:

The Match function should return the type specified in the first matching pattern. If no pattern matches, it should return never.

Edge Cases to Consider:

  • Empty patterns array.
  • subject type that is never.
  • Patterns that are structurally equivalent but have different names.
  • Patterns that are more specific than others (e.g., string vs. "hello"). The more specific pattern should take precedence.

Examples

Example 1:

type Subject = string;
type Patterns = [
  [string, number],
  [number, boolean],
  [symbol, null]
];

type Result = Match<Subject, Patterns>; // Result should be number

Explanation: The Subject is a string. The first pattern in Patterns is [string, number], which matches. Therefore, the result is number.

Example 2:

type Subject = { name: string; age: number };
type Patterns = [
  [{ name: string; age: number }, boolean],
  [{ name: string }, string],
  [number, null]
];

type Result = Match<Subject, Patterns>; // Result should be boolean

Explanation: The Subject is { name: string; age: number }. The first pattern matches exactly. Therefore, the result is boolean.

Example 3:

type Subject = { name: string };
type Patterns = [
  [{ name: string; age: number }, boolean],
  [{ name: string }, string],
  [number, null]
];

type Result = Match<Subject, Patterns>; // Result should be string

Explanation: The Subject is { name: string }. The second pattern matches. Therefore, the result is string.

Example 4:

type Subject = never;
type Patterns = [
  [string, number],
  [number, boolean]
];

type Result = Match<Subject, Patterns>; // Result should be never

Explanation: The Subject is never. Neither pattern matches. Therefore, the result is never.

Constraints

  • The patterns array can contain a maximum of 10 patterns. This is to prevent excessive type complexity and potential compiler errors.
  • All types within the patterns array must be valid TypeScript types.
  • The Match function should be reasonably performant. While type-level computations are inherently slower than runtime computations, avoid unnecessary complexity that could significantly impact compilation time.
  • The patterns array must be a tuple of tuples. Incorrect formatting will lead to type errors.

Notes

  • Consider using a recursive conditional type to iterate through the patterns array.
  • The core of the solution lies in effectively comparing types using conditional types. Think about how to express structural equality at the type level.
  • The order of patterns in the patterns array matters. The first matching pattern will be the result.
  • This challenge is designed to be difficult and requires a deep understanding of TypeScript's type system. Don't be afraid to experiment and consult the TypeScript documentation.
  • You can use utility types like infer to extract type information within conditional types.
Loading editor...
typescript