TypeScript Обновление v5

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 продолжает работать так, как описано в разделе о классах.