Length of String
Завдання
Вирахуйте довжину рядкового літерала. Наприклад:
type length = LengthOfString<"Hello, World">; // expected to be 12
Розв’язок
Спочатку я спробував найпростіше рішення – звернутись до властивості length
через індексні типи. Сподівався, що TypeScript достатньо розумний, щоб повернути
значення:
type LengthOfString<S extends string> = S["length"];
На жаль, ні. Таким чином ми отримаємо number
, але не числовий літерал. Отже,
потрібно придумати інше рішення.
А що, якщо ми виведемо перший символ та решту символів рекурсивно, доти, доки не залишиться символів в рядку? В такому випадку, ми отримаємо лічильник, який працює на рекурсії. Почнемо з типу, який виводить перший символ та решту рядка (хвіст):
type LengthOfString<S extends string> = S extends `${infer C}${infer T}`
? never
: never;
В тип-параметрі C
отримуємо перший символ та у T
отримуємо хвіст. Викликаючи
тип LengthOfString
рекурсивно з рештою рядка, ми зупинимось у випадку, коли
символів більше не залишиться.
type LengthOfString<S extends string> = S extends `${infer C}${infer T}`
? LengthOfString<T>
: never;
Проблема в тому, що ми не знаємо, де зберігати наш лічильник. Очевидно, ми
можемо додати інший тип параметр до LengthOfString
, який буде акумулювати
значення, але TypeScript не надає інструментів для керування числами в системі
типів.
Замість чисел створимо кортеж і будемо додавати по одному символу на кожній ітерації.
type LengthOfString<
S extends string,
A extends string[],
> = S extends `${infer C}${infer T}` ? LengthOfString<T, [C, ...A]> : never;
Перетворюємо рядковий літерал в кортеж символів цього рядкового літерала. Як тільки в рядковому літералі не залишається символів – повертаємо довжину кортежу.
type LengthOfString<
S extends string,
A extends string[],
> = S extends `${infer C}${infer T}`
? LengthOfString<T, [C, ...A]>
: A["length"];
Додавши новий тип параметр ми зламали тести. Тому, що ми передаємо два типи параметри замість одного. Виправляємо це, додавши до другого параметру значення за замовчуванням – порожній кортеж.
type LengthOfString<
S extends string,
A extends string[] = [],
> = S extends `${infer C}${infer T}`
? LengthOfString<T, [C, ...A]>
: A["length"];
Коментарі