TypeScript активно развивается и поддерживается корпорацией Microsoft.
В версии 5.x произошло множество обновлений, направленных на повышение удобства и качества работы с языком.
В этой главе мы рассмотрим наиболее популярные обновления, позволяющие усилить контроль над типами и повысить гибкость.
Напоминаем, что эти функции доступны только в версиях 5.x и выше.
Типы на основе шаблонной строки
Типы на основе шаблонной строки теперь позволяют создавать более точные типы с использованием шаблонных выражений.
Мы можем определять пользовательские типы, которые зависят от фактических значений строк во время компиляции.
Пример
Задача: создать тип, допускающий только строки вида User: <имя>.
type UserGreeting = `User: ${string}`;
const valid: UserGreeting = "User: Alice"; // OK
const invalid: UserGreeting = "Alice"; // Ошибка: не соответствует шаблону
Пояснение:
${string}— подстановка любого строкового значения;- префикс
"User: "жёстко задан; - компилятор проверяет соответствие при присваивании.
Пример
Задача: ограничить возможные роли пользователя строками admin:<уровень>, где уровень — basic или pro.
type Role = "basic" | "pro";
type AdminRole = `admin:${Role}`;
const adminBasic: AdminRole = "admin:basic"; // OK
const adminPro: AdminRole = "admin:pro"; // OK
const adminInvalid: AdminRole = "admin:premium"; // Ошибка: "premium" не входит в Role
Пояснение:
Role— объединение литералов;- в
AdminRoleподстановка${Role}принимает только значения из объединения.
Пример
Задача: сформировать тип для URL‑путей вида /<ресурс>/<id>, где ресурс — user или post, а id — число.
type Resource = "user" | "post";
type Path = `/${Resource}/${number}`;
const userPath: Path = "/user/123"; // OK
const postPath: Path = "/post/456"; // OK
const invalidPath: Path = "/comment/789"; // Ошибка: "comment" не в Resource
Пояснение:
- две подстановки:
${Resource}и${number}; - компилятор проверяет оба параметра одновременно.
Пример
Задача: создать тип, преобразующий строку kebab-case в camelCase (упрощённый вариант).
type KebabToCamel<T extends string> = T extends `${infer P1}-${infer P2}`
? `${P1}${Capitalize<P2>}`
: T;
type Test1 = KebabToCamel<"hello-world">; // "helloWorld"
type Test2 = KebabToCamel<"foo">; // "foo" (нет дефиса — возвращается как есть)
Пояснение:
infer— механизм вывода типов в шаблонах;- рекурсивная подстановка заменяет первый дефис;
Capitalize— встроенная утилита TypeScript для заглавной буквы.
Метки индексной сигнатуры
Метки индексной сигнатуры позволяют использовать вычисляемые имена свойств для меток индексируемых сигнатур.
Это помогает предоставлять более подробную информацию о типах при работе с динамическими объектами.
Пример
Задача: создать тип для объекта, где любые строковые ключи соответствуют строковым значениям.
type StringDictionary = { [key: string]: string };
const userData: StringDictionary = {
name: "Alice",
city: "Moscow",
job: "Developer"
}; // OK: все ключи — строки, все значения — строки
const invalid: StringDictionary = {
age: 30 // Ошибка: число не соответствует типу string
};
Пояснение:
[key: string]— метка индексной сигнатуры: разрешает любые строковые ключи;stringпосле:— тип значений (обязательно строки);- компилятор проверяет, что все значения в объекте — строки.
Пример
Задача: описать тип для объекта, где ключи — числа (индексы), а значения — количества (тоже числа).
type NumberCounter = { [index: number]: number };
const scores: NumberCounter = {
0: 10,
1: 25,
2: 7
}; // OK: ключи — числа, значения — числа
const mixed: NumberCounter = {
"total": 100 // Ошибка: строка "total" не соответствует типу number
};
Пояснение:
[index: number]— метка: ключи должны быть числами (как в массивах);numberпосле:— тип значений (тоже числа);- строка
"total"недопустима как ключ — компилятор выдаст ошибку.
Пример
Задача: определить тип для объекта, где ключи — строки, а значения могут быть либо строкой, либо числом.
type FlexibleObject = { [key: string]: string | number };
const profile: FlexibleObject = {
name: "Bob",
age: 35,
city: "Paris"
}; // OK: строки и числа допустимы
const invalidProfile: FlexibleObject = {
isActive: true // Ошибка: boolean не входит в string | number
};
Пояснение:
[key: string]— разрешает любые строковые ключи;string | number— объединение типов: значения могут быть строкой или числом;true(boolean) не входит в объединение — компилятор отметит ошибку.
Пример
type DynamicObject = { [key: `dynamic_${string}`]: string };
// Использование:
let obj: DynamicObject = { dynamic_key: "value" };
Нативные приватные поля
Версия 5.x также добавила поддержку нативных приватных полей JavaScript.
Тип private в TypeScript продолжает работать так, как описано в разделе о классах.