Hone logo
Hone
Problems

Type-Level Maps in TypeScript

This challenge focuses on creating robust and flexible type-level maps in TypeScript. Type-level programming allows us to perform computations and manipulations at the type level, leading to more powerful static analysis and safer code. You will implement a way to represent a map where both keys and values are strictly typed, and operations on this map are also type-checked.

Problem Description

Your task is to define a TypeScript type, let's call it TypeMap<Keys extends string | number | symbol, Values>, that behaves like a map. This type should accept a union of possible keys and a type representing the corresponding values.

Specifically, you need to achieve the following:

  1. Define TypeMap: This type should take two type parameters:

    • K: A union of keys (e.g., 'a' | 'b').
    • V: The type of the values associated with each key. For simplicity in this challenge, assume V will be a union of possible value types, or a single type that all values conform to.
  2. Representing the Map Structure: The TypeMap should internally represent a structure where each key from K is associated with a value of type V.

  3. Key Requirement: The primary goal is to enable type-safe access to these "mapped" values. You should also consider how to represent the absence of a key.

  4. Expected Behavior:

    • When a TypeMap is defined with a specific set of keys and a value type, its structure should reflect this mapping statically.
    • You should be able to create types that represent looking up a key in the TypeMap and getting its corresponding value type.
  5. Edge Cases:

    • What happens if a key is accessed that is not part of the TypeMap's defined keys? The resulting type should indicate this, perhaps by resolving to never or a specific "not found" type.
    • Consider an empty set of keys.

Examples

Example 1: Basic Mapping

// Input Type Definition
type MyStringMap = TypeMap<"name" | "age", string | number>;

// Expected Type Structure (conceptual - not actual TS output, but what it represents)
// {
//   name: string | number;
//   age: string | number;
// }

// Type-safe lookup
type NameType = MyStringMap["name"]; // Expected: string | number
type AgeType = MyStringMap["age"];   // Expected: string | number

// Type-safe access for a non-existent key
type CityType = MyStringMap["city"]; // Expected: never (or a designated "not found" type)

Example 2: Mapping with Different Value Types (Simplified)

For this challenge, we'll simplify the value type. Let's assume all values for a given TypeMap instance share a common base type, or are part of a union.

// Input Type Definition
type UserDataMap = TypeMap<"id" | "username", string>;

// Expected Type Structure (conceptual)
// {
//   id: string;
//   username: string;
// }

// Type-safe lookup
type UserIdType = UserDataMap["id"];       // Expected: string
type UsernameType = UserDataMap["username"]; // Expected: string

Example 3: Empty Keys

// Input Type Definition
type EmptyMap = TypeMap<never, string>;

// Expected Type Structure (conceptual)
// {}

// Type-safe lookup on an empty map
type AnyKeyType = EmptyMap["someKey"]; // Expected: never

Constraints

  • The TypeMap type must be implemented using only TypeScript's built-in conditional types, mapped types, and distributive conditional types. You cannot use external libraries or runtime code.
  • The Keys type parameter should accept a union of string, number, or symbol.
  • The Values type parameter can be a single type or a union of types.
  • The solution should be purely at the type level; no runtime JavaScript code is expected.

Notes

This challenge is about leveraging TypeScript's advanced type system to build data structures that are validated at compile time. Think about how you can iterate over a union of types at the type level and construct a new type that reflects the mapping. Consider using mapped types and conditional types to achieve this. The goal is to create a type that, when instantiated, behaves like an object literal at the type level, but with the added benefit of compile-time checks for keys and their associated value types.

Loading editor...
typescript