Hone logo
Hone
Problems

Type-Level Module Resolution in TypeScript

This challenge asks you to implement a type-level mechanism for resolving module imports within a simplified TypeScript-like environment. Type-level programming allows us to perform computations and manipulations on types themselves, rather than values at runtime. This is useful for tasks like ensuring type safety, generating code, and performing advanced type transformations.

Problem Description

You are tasked with creating a type-level module resolution system. Imagine a simplified TypeScript where modules are represented as type aliases, and imports are represented as conditional types. Your goal is to create a utility type resolveModule<Module, ImportName, ResolvedType> that, given a module type, an import name (a string literal type), and a type representing the resolved type, determines if the import exists within the module and returns the resolved type if it does, otherwise returns never.

The module type will be a type alias that can contain a single property named exports. The exports property will be a union of string literal types, each representing a public export name. The ResolvedType argument represents the type to return if the import is found.

Key Requirements:

  • The resolveModule type must be implemented using conditional types and type inference.
  • It should accurately determine if the import name exists within the module's exports.
  • If the import exists, it should return the ResolvedType.
  • If the import does not exist, it should return never.
  • The module type must conform to the structure described above (type alias with an optional exports property which is a union of string literal types).

Expected Behavior:

The type resolveModule should behave as a type-level lookup function. It should effectively check if a given export name is present in the module's exports and return the corresponding resolved type if found.

Edge Cases to Consider:

  • Empty modules (modules with no exports property).
  • Modules with no exports (modules where exports is an empty union).
  • Import names that are not string literal types.
  • Modules with exports that are not string literal types (should be handled gracefully, ideally returning never).

Examples

Example 1:

type Module1 = {
  exports?: "foo" | "bar";
};

type ResolvedFoo = string;
type ResolvedBar = number;
type ResolvedBaz = never;

type Resolved = resolveModule<Module1, "foo", ResolvedFoo>; // Resolved should be string
type Resolved2 = resolveModule<Module1, "bar", ResolvedBar>; // Resolved2 should be number
type Resolved3 = resolveModule<Module1, "baz", ResolvedBaz>; // Resolved3 should be never

Example 2:

type Module2 = {
  exports?: "hello" | "world" | "typescript";
};

type ResolvedHello = boolean;
type ResolvedWorld = null;
type ResolvedTypeScript = undefined;
type ResolvedGoodbye = never;

type Resolved4 = resolveModule<Module2, "hello", ResolvedHello>; // Resolved4 should be boolean
type Resolved5 = resolveModule<Module2, "world", ResolvedWorld>; // Resolved5 should be null
type Resolved6 = resolveModule<Module2, "typescript", ResolvedTypeScript>; // Resolved6 should be undefined
type Resolved7 = resolveModule<Module2, "goodbye", ResolvedGoodbye>; // Resolved7 should be never

Example 3: (Empty Module)

type Module3 = {};

type ResolvedEmpty = never;

type Resolved8 = resolveModule<Module3, "anything", ResolvedEmpty>; // Resolved8 should be never

Constraints

  • The solution must be implemented using TypeScript's type system (conditional types, union types, etc.).
  • The resolveModule type must be generic, accepting the module type, import name, and resolved type as type parameters.
  • The import name must be a string literal type.
  • The solution should be reasonably performant (avoiding excessive type recursion that could lead to compiler errors). While performance isn't the primary concern, excessively complex type logic should be avoided.
  • The module type must adhere to the specified structure (type alias with an optional exports property which is a union of string literal types).

Notes

  • Consider using distributive conditional types to effectively check for the existence of the import name within the union of exports.
  • Think about how to handle the case where the exports property is missing from the module type.
  • This is a type-level problem, so no runtime code will be executed. The goal is to define a type that correctly resolves the import based on the module's type definition.
  • The ResolvedType is a placeholder; the actual type returned should be determined by the module's exports.
Loading editor...
typescript