Дослідіть потужні типи template literal в TypeScript для розширеної обробки рядків, зіставлення за шаблоном і валідації. Навчіться на практичних прикладах і реальних випадках використання.
Типи Template Literal: Зіставлення рядків за шаблоном і валідація в TypeScript
Система типів TypeScript постійно розвивається, пропонуючи розробникам потужніші інструменти для вираження складної логіки та забезпечення безпеки типів. Однією з найцікавіших і універсальних функцій, представлених в останніх версіях, є типи template literal. Ці типи дозволяють маніпулювати рядками на рівні типів, забезпечуючи розширене зіставлення рядків за шаблоном і валідацію. Це відкриває цілий новий світ можливостей для створення більш надійних і підтримуваних додатків.
Що таке типи Template Literal?
Типи template literal - це форма типу, яка конструюється шляхом об'єднання типів рядкових літералів і типів об'єднання, подібно до того, як template literals працюють у JavaScript. Однак, замість створення рядків під час виконання, вони створюють нові типи на основі існуючих.
Ось базовий приклад:
type Greeting<T extends string> = `Hello, ${T}!`;
type MyGreeting = Greeting<"World">; // type MyGreeting = "Hello, World!"
У цьому прикладі `Greeting` — це тип template literal, який приймає тип рядка `T` як вхідні дані та повертає новий тип, який є конкатенацією "Hello, ", `T` і "!".
Базове зіставлення рядків за шаблоном
Типи template literal можна використовувати для виконання базового зіставлення рядків за шаблоном. Це дозволяє створювати типи, які є дійсними, лише якщо вони відповідають певному шаблону.
Наприклад, ви можете створити тип, який приймає лише рядки, що починаються з "prefix-":
type PrefixedString<T extends string> = T extends `prefix-${string}` ? T : never;
type ValidPrefixedString = PrefixedString<"prefix-valid">; // type ValidPrefixedString = "prefix-valid"
type InvalidPrefixedString = PrefixedString<"invalid">; // type InvalidPrefixedString = never
У цьому прикладі `PrefixedString` використовує умовний тип, щоб перевірити, чи вхідний рядок `T` починається з "prefix-". Якщо так, то типом є сам `T`; інакше це `never`. `never` — це спеціальний тип у TypeScript, який представляє тип значень, які ніколи не трапляються, ефективно виключаючи недійсний рядок.
Вилучення частин рядка
Типи template literal також можна використовувати для вилучення частин рядка. Це особливо корисно, коли вам потрібно розібрати дані з рядків і перетворити їх на різні типи.
Припустимо, у вас є рядок, який представляє координату у форматі "x:10,y:20". Ви можете використовувати типи template literal, щоб вилучити значення x та y:
type CoordinateString = `x:${number},y:${number}`;
type ExtractX<T extends CoordinateString> = T extends `x:${infer X},y:${number}` ? X : never;
type ExtractY<T extends CoordinateString> = T extends `x:${number},y:${infer Y}` ? Y : never;
type XValue = ExtractX<"x:10,y:20">; // type XValue = 10
type YValue = ExtractY<"x:10,y:20">; // type YValue = 20
У цьому прикладі `ExtractX` і `ExtractY` використовують ключове слово `infer` для захоплення частин рядка, які відповідають типу `number`. `infer` дозволяє витягти тип із зіставлення за шаблоном. Захоплені типи потім використовуються як тип повернення умовного типу.
Розширена валідація рядків
Типи template literal можна поєднувати з іншими функціями TypeScript, такими як типи об'єднання та умовні типи, для виконання розширеної валідації рядків. Це дозволяє створювати типи, які застосовують складні правила до структури та вмісту рядків.
Наприклад, ви можете створити тип, який перевіряє рядки дат ISO 8601:
type Year = `${number}${number}${number}${number}`;
type Month = `0${number}` | `10` | `11` | `12`;
type Day = `${0}${number}` | `${1 | 2}${number}` | `30` | `31`;
type ISODate = `${Year}-${Month}-${Day}`;
type ValidDate = ISODate extends "2023-10-27" ? true : false; // true
type InvalidDate = ISODate extends "2023-13-27" ? true : false; // false
function processDate(date: ISODate) {
// Function logic here. TypeScript enforces the ISODate format.
return `Processing date: ${date}`;
}
console.log(processDate("2024-01-15")); // Works
//console.log(processDate("2024-1-15")); // TypeScript error: Argument of type '"2024-1-15"' is not assignable to parameter of type '`${number}${number}${number}${number}-${0}${number}-${0}${number}` | `${number}${number}${number}${number}-${0}${number}-${1}${number}` | ... 14 more ... | `${number}${number}${number}${number}-12-31`'.
Тут `Year`, `Month` і `Day` визначені з використанням типів template literal для представлення дійсних форматів для кожної частини дати. Потім `ISODate` об'єднує ці типи для створення типу, який представляє дійсний рядок дати ISO 8601. Приклад також демонструє, як цей тип можна використовувати для забезпечення форматування даних у функції, запобігаючи передачі неправильних форматів дат. Це покращує надійність коду та запобігає помилкам під час виконання, викликаним недійсними вхідними даними.
Реальні випадки використання
Типи template literal можна використовувати в різних реальних сценаріях. Ось кілька прикладів:
- Валідація форм: Ви можете використовувати типи template literal для перевірки формату вхідних даних форми, таких як адреси електронної пошти, номери телефонів і поштові індекси.
- Валідація запитів API: Ви можете використовувати типи template literal для перевірки структури корисних навантажень запитів API, гарантуючи, що вони відповідають очікуваному формату. Наприклад, валідація коду валюти (наприклад, "USD", "EUR", "GBP").
- Аналіз файлів конфігурації: Ви можете використовувати типи template literal для аналізу файлів конфігурації та вилучення значень на основі конкретних шаблонів. Розгляньте можливість валідації шляхів до файлів в об'єкті конфігурації.
- Енумератори на основі рядків: Ви можете створювати енумератори на основі рядків із валідацією за допомогою типів template literal.
Приклад: Валідація кодів валют
Давайте розглянемо більш детальний приклад валідації кодів валют. Ми хочемо переконатися, що у нашому додатку використовуються лише дійсні коди валют ISO 4217. Ці коди зазвичай складаються з трьох великих літер.
type CurrencyCode = `${Uppercase<string>}${Uppercase<string>}${Uppercase<string>}`;
function formatCurrency(amount: number, currency: CurrencyCode) {
// Function logic to format currency based on the provided code.
return `$${amount} ${currency}`;
}
console.log(formatCurrency(100, "USD")); // Works
//console.log(formatCurrency(100, "usd")); // TypeScript error: Argument of type '"usd"' is not assignable to parameter of type '`${Uppercase}${Uppercase}${Uppercase}`'.
//More precise example:
type ValidCurrencyCode = "USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"; // Extend as needed
type StronglyTypedCurrencyCode = ValidCurrencyCode;
function formatCurrencyStronglyTyped(amount: number, currency: StronglyTypedCurrencyCode) {
return `$${amount} ${currency}`;
}
console.log(formatCurrencyStronglyTyped(100, "EUR")); // Works
//console.log(formatCurrencyStronglyTyped(100, "CNY")); // TypeScript error: Argument of type '"CNY"' is not assignable to parameter of type '"USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"'.
Цей приклад демонструє, як створити тип `CurrencyCode`, який приймає лише рядки, що складаються з трьох великих літер. Другий, більш строго типізований приклад показує, як обмежити це ще більше до попередньо визначеного списку прийнятних валют.
Приклад: Валідація шляхів кінцевих точок API
Іншим випадком використання є валідація шляхів кінцевих точок API. Ви можете визначити тип, який представляє дійсну структуру кінцевої точки API, гарантуючи, що запити надсилаються за правильними шляхами. Це особливо корисно в мікросервісних архітектурах, де кілька сервісів можуть надавати різні API.
type APIServiceName = "users" | "products" | "orders";
type APIEndpointPath = `/${APIServiceName}/${string}`;
function callAPI(path: APIEndpointPath) {
// API call logic
console.log(`Calling API: ${path}`);
}
callAPI("/users/123"); // Valid
callAPI("/products/details"); // Valid
//callAPI("/invalid/path"); // TypeScript error
// Even more specific:
type APIAction = "create" | "read" | "update" | "delete";
type APIEndpointPathSpecific = `/${APIServiceName}/${APIAction}`;
function callAPISpecific(path: APIEndpointPathSpecific) {
// API call logic
console.log(`Calling specific API: ${path}`);
}
callAPISpecific("/users/create"); // Valid
//callAPISpecific("/users/list"); // TypeScript error
Це дозволяє точніше визначити структуру кінцевих точок API, запобігаючи друкарським помилкам і забезпечуючи узгодженість у вашому додатку. Це базовий приклад; можна створити більш складні шаблони для валідації параметрів запиту та інших частин URL-адреси.
Переваги використання типів Template Literal
Використання типів template literal для зіставлення рядків за шаблоном і валідації пропонує кілька переваг:
- Покращена безпека типів: Типи template literal дозволяють застосовувати більш суворі обмеження типів до рядків, зменшуючи ризик помилок під час виконання.
- Покращена читабельність коду: Типи template literal роблять ваш код більш читабельним, чітко виражаючи очікуваний формат рядків.
- Підвищена зручність у супроводі: Типи template literal роблять ваш код більш зручним у супроводі, надаючи єдине джерело істини для правил валідації рядків.
- Кращий досвід розробника: Типи template literal забезпечують краще автозавершення та повідомлення про помилки, покращуючи загальний досвід розробника.
Обмеження
Хоча типи template literal є потужними, вони також мають деякі обмеження:
- Складність: Типи template literal можуть стати складними, особливо при роботі зі складними шаблонами. Важливо збалансувати переваги безпеки типів із зручністю супроводу коду.
- Продуктивність: Типи template literal можуть впливати на продуктивність компіляції, особливо у великих проектах. Це тому, що TypeScript потрібно виконувати більш складну перевірку типів.
- Обмежена підтримка регулярних виразів: Хоча типи template literal дозволяють зіставляти за шаблоном, вони не підтримують повний спектр функцій регулярних виразів. Для дуже складної валідації рядків під час виконання регулярні вирази все ще можуть знадобитися разом із цими конструкціями типів для належної очистки вхідних даних.
Найкращі практики
Ось кілька найкращих практик, про які слід пам'ятати під час використання типів template literal:
- Почніть з простого: Почніть з простих шаблонів і поступово збільшуйте складність за потреби.
- Використовуйте описові імена: Використовуйте описові імена для своїх типів template literal, щоб покращити читабельність коду.
- Документуйте свої типи: Документуйте свої типи template literal, щоб пояснити їх призначення та використання.
- Ретельно тестуйте: Ретельно тестуйте свої типи template literal, щоб переконатися, що вони поводяться належним чином.
- Враховуйте продуктивність: Пам'ятайте про вплив типів template literal на продуктивність компіляції та оптимізуйте свій код відповідно.
Висновок
Типи template literal - це потужна функція в TypeScript, яка дозволяє виконувати розширену обробку рядків, зіставлення за шаблоном і валідацію на рівні типів. Використовуючи типи template literal, ви можете створювати більш надійні, зручні в супроводі та безпечні за типами програми. Хоча вони мають деякі обмеження, переваги використання типів template literal часто переважують недоліки, що робить їх цінним інструментом в арсеналі будь-якого розробника TypeScript. Оскільки мова TypeScript продовжує розвиватися, розуміння та використання цих розширених функцій типу буде мати вирішальне значення для створення високоякісного програмного забезпечення. Пам'ятайте про збалансування складності з читабельністю та завжди надавайте пріоритет ретельному тестуванню.