Type-Level String Manipulation: Extracting and Concatenating
Type-level programming in TypeScript allows you to perform operations on types themselves, rather than values at runtime. This challenge focuses on creating type-level functions to manipulate strings represented as types. Specifically, you'll implement functions to extract a substring and concatenate two string types, demonstrating the power of conditional types and utility types. This is useful for tasks like generating type-safe API endpoints based on configuration or creating specialized types based on string patterns.
Problem Description
You are tasked with creating two type-level functions: substring and concatenate.
-
substring<T extends string, Start extends number, Length extends number>: This function should extract a substring of lengthLengthstarting at indexStartfrom the string typeT. The result should be a new string type. IfStartorLengthare out of bounds, return an empty string type (""). Note that TypeScript string indices are 0-based. -
concatenate<T extends string, U extends string>: This function should concatenate the string typesTandUinto a single string type.
Key Requirements:
- Both functions must operate entirely at the type level, without runtime execution.
- The functions should be generic, accepting string types and number types as arguments.
- Error handling for out-of-bounds indices in
substringis crucial. - The functions should be type-safe, ensuring that the input types are valid strings and numbers.
Expected Behavior:
The functions should produce the expected string types based on the input types. Incorrect indices or invalid types should result in appropriate type errors or empty string types.
Edge Cases to Consider:
Startis negative.Startis greater than or equal to the length ofT.Lengthis negative.Lengthis greater than the remaining length ofTstarting fromStart.- Empty string types as input.
- Zero length substring.
Examples
Example 1:
type Result1 = substring<"abcdefg", 2, 3>; // "cde"
Explanation: Extracts 3 characters starting from index 2 ("c", "d", "e") from "abcdefg".
Example 2:
type Result2 = substring<"abcdefg", 5, 2>; // "fg"
Explanation: Extracts 2 characters starting from index 5 ("f", "g") from "abcdefg".
Example 3:
type Result3 = substring<"abcdefg", 2, 10>; // ""
Explanation: Length (10) is greater than the remaining length of the string starting from index 2. Returns an empty string.
Example 4:
type Result4 = substring<"abcdefg", -1, 3>; // ""
Explanation: Start is negative. Returns an empty string.
Example 5:
type Result5 = concatenate<"hello", " world">; // "hello world"
Explanation: Concatenates "hello" and " world" to produce "hello world".
Example 6:
type Result6 = concatenate<"", "typescript">; // "typescript"
Explanation: Concatenates an empty string with "typescript" to produce "typescript".
Constraints
- All input types must be valid string types or number types.
StartandLengthmust be non-negative integers.- The functions should be implemented using TypeScript's type system features (conditional types, utility types, etc.).
- Performance is not a concern, as these operations are performed at compile time.
Notes
- Consider using conditional types to handle out-of-bounds scenarios in
substring. - You might find it helpful to define smaller, reusable type-level utility functions to build up the more complex functions.
- Think about how to represent string indices and lengths as types.
- The goal is to create type-safe and expressive type-level functions. Focus on correctness and clarity over extreme optimization.