Hone logo
Hone
Problems

Path Mapping Types in TypeScript

This challenge focuses on creating TypeScript types to represent and manipulate path mappings. Path mappings are crucial in build tools and module resolution, allowing you to alias or redirect paths within your project. Successfully completing this challenge will demonstrate your understanding of advanced TypeScript type manipulation and utility types.

Problem Description

You are tasked with defining TypeScript types that represent path mappings and provide utility types for working with them. A path mapping is a key-value pair where the key is a source path and the value is a destination path. You need to create the following types:

  1. PathMapping: A type representing a single path mapping. It should be a readonly tuple of two strings: [source: string, destination: string].
  2. PathMappings: A type representing a collection of path mappings. It should be a readonly tuple of PathMapping tuples.
  3. ResolvePathMapping<T extends PathMappings, Path>: A generic utility type that takes a PathMappings tuple and a Path string as input. It should return a Path string if the input Path exists as a source in the PathMappings. If the Path is not found, it should return the original Path string.
  4. MergePathMappings<T extends PathMappings, U extends PathMappings>: A generic utility type that merges two PathMappings tuples into a single PathMappings tuple. If there are duplicate source paths, the destination from the second tuple (U) should take precedence.

Examples

Example 1:

Input:
const mappings: PathMappings = [
  ["src/components", "dist/components"],
  ["src/utils", "dist/utils"],
];

const resolvedPath = ResolvePathMapping<PathMappings, "src/components">;
const unresolvedPath = ResolvePathMapping<PathMappings, "src/other"];

Output:
Type of resolvedPath: "dist/components"
Type of unresolvedPath: "src/other"

Explanation: The ResolvePathMapping type correctly resolves "src/components" to "dist/components" based on the provided mappings, and leaves "src/other" unchanged as it's not a defined source.

Example 2:

Input:
const mappings1: PathMappings = [
  ["src/components", "dist/components"],
  ["src/utils", "dist/utils"],
];

const mappings2: PathMappings = [
  ["src/utils", "new/utils"],
  ["src/api", "dist/api"],
];

const mergedMappings = MergePathMappings<PathMappings, PathMappings>;

Output:
Type of mergedMappings: readonly [
  readonly ["src/components", "dist/components"],
  readonly ["src/utils", "new/utils"],
  readonly ["src/api", "dist/api"]
]

Explanation: The MergePathMappings type correctly merges the two mapping sets. Note that "src/utils" is updated to "new/utils" because it appears later in the second mapping set.

Example 3: (Edge Case - Empty Mappings)

Input:
const emptyMappings: PathMappings = [];
const resolvedPath = ResolvePathMapping<PathMappings, "src/components">;

Output:
Type of resolvedPath: "src/components"

Explanation: When the mappings are empty, the ResolvePathMapping type should return the original path unchanged.

Constraints

  • All paths (source and destination) are strings.
  • PathMappings tuples are readonly.
  • The order of mappings in PathMappings matters for MergePathMappings (later mappings override earlier ones for duplicate sources).
  • The ResolvePathMapping type should not perform runtime evaluation; it should be a purely type-level operation.
  • MergePathMappings should not produce duplicate source paths in the merged result.

Notes

  • Consider using conditional types and mapped types to achieve the desired type transformations.
  • Think about how to handle the case where a path is not found in the mappings.
  • The MergePathMappings type should be implemented in a way that avoids creating duplicate source paths. The order of the input tuples matters for resolving conflicts.
  • Focus on creating accurate and well-typed utility types. Runtime behavior is not a concern for this challenge.
Loading editor...
typescript