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:
PathMapping: A type representing a single path mapping. It should be a readonly tuple of two strings:[source: string, destination: string].PathMappings: A type representing a collection of path mappings. It should be a readonly tuple ofPathMappingtuples.ResolvePathMapping<T extends PathMappings, Path>: A generic utility type that takes aPathMappingstuple and aPathstring as input. It should return aPathstring if the inputPathexists as a source in thePathMappings. If thePathis not found, it should return the originalPathstring.MergePathMappings<T extends PathMappings, U extends PathMappings>: A generic utility type that merges twoPathMappingstuples into a singlePathMappingstuple. 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.
PathMappingstuples are readonly.- The order of mappings in
PathMappingsmatters forMergePathMappings(later mappings override earlier ones for duplicate sources). - The
ResolvePathMappingtype should not perform runtime evaluation; it should be a purely type-level operation. MergePathMappingsshould 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
MergePathMappingstype 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.