Menu

Шаблонные литеральные типы в TypeScript: синтаксис и примеры

Шаблонные литеральные типы строят строковые литеральные типы с тем же синтаксисом обратных кавычек, что и шаблонные строки JavaScript: on${Capitalize<E>}. Синтаксис, как перемножаются объединения, Uppercase и Capitalize, шаблоны вроде ${number}px, геттеры в сопоставленных типах и разбор строк через infer.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Шаблонный литеральный тип строит строковые литеральные типы с тем же синтаксисом обратных кавычек, что и шаблонная строка JavaScript. `on${Capitalize<"click" | "focus">}` это тип "onClick" | "onFocus", вычисленный компилятором:

Шаблонные литеральные типы существуют только при компиляции. Они проверяют строковые литералы и типизированные значения, пока вы пишете код, и ничего не добавляют в вывод JavaScript. Таблица обработчиков выше использует Record, чтобы требовать по функции на каждое имя.

Синтаксис

Внутри обратных кавычек пишется литеральный текст и заполнители ${...}. Заполнитель содержит тип, а не значение: строковый, числовой, bigint или логический литеральный тип, их объединение или один из широких типов string, number, bigint, boolean, null и undefined.

Заполнитель с широким типом вроде string или number создаёт шаблон: тип остаётся `hello ${string}`, и принимается любая подходящая строка. Заполнитель с конечным объединением, например boolean, раскрывается в его члены.

Объединения перемножаются

С несколькими объединениями результатом будут все комбинации:

Три размера на два тона дают шесть членов. Число растёт быстро: пять заполнителей, каждый с объединением из десяти букв, дали бы 100 000 членов, и TypeScript отказывается с ошибкой error TS2590: Expression produces a union type that is too complex to represent. Используйте широкий заполнитель вроде ${string}, когда не нужны все точные значения.

Uppercase, Lowercase, Capitalize, Uncapitalize

Четыре встроенных типа меняют регистр строковых литеральных типов. Они встроенные (intrinsic): реализованы внутри компилятора, а не написаны на TypeScript.

ТипВходРезультат
Uppercase<S>"hello world""HELLO WORLD"
Lowercase<S>"Content-Type""content-type"
Capitalize<S>"hello world""Hello world"
Uncapitalize<S>"UserName""userName"

Они меняют только типы. Чтобы построить соответствующую строку во время выполнения, по-прежнему вызывайте toUpperCase() или сами делите строку и меняйте регистр, а затем сообщите TypeScript, что у результата точный тип:

as нужен потому, что toUpperCase() типизирован как возвращающий обычный string. Вызывающий код видит сигнатуру функции, поэтому capitalize("report") имеет литеральный тип "Report".

Шаблоны строк: ${number}px и другие

Тип-шаблон принимает любую строку заданной формы. Это удобно для значений CSS, идентификаторов и ключей с известным префиксом:

${number} принимает любую строку, которую JavaScript читает как число, а это мягче, чем кажется: "-3px", "1e3px" и "0x10px" проходят проверку типов. Считайте такие шаблоны защитой от опечаток в литералах, а не полноценной валидацией.

Шаблонные литералы в сопоставленных типах

Больше всего пользы от шаблонных литеральных типов в конструкции as сопоставленного типа, где они порождают имена свойств из других имён свойств:

string & K оставляет только строковые ключи, поскольку Capitalize не принимает числа и символы. Каждый колбэк получает тип параметра из свойства, за которым он следит.

Разбор строк через infer

В условном типе шаблонный литерал может сопоставить строку и захватить её части через infer. Так извлекаются имена параметров из шаблона маршрута:

Если пропустить postId в вызове, компилятор сообщит, что его не хватает.

Шаблонные выражения расширяются до string

Выражение с шаблонной строкой в обычном коде имеет тип string, даже если все его части это литеральные типы. Добавьте as const, чтобы сохранить шаблонный литеральный тип:

Без as const присваивание loose в `log:${Level}` не проходит, потому что string может быть чем угодно.

Часто задаваемые вопросы

Что такое шаблонные литеральные типы в TypeScript?

Строковые литеральные типы, записанные в обратных кавычках с заполнителями ${...}, как шаблонные строки JavaScript, но на уровне типов. type Greeting = `hello ${string}` принимает любую строку, начинающуюся с hello , а `on${Capitalize<"click">}` это литеральный тип "onClick".

Что происходит, если поместить объединение в шаблонный литеральный тип?

Шаблон раскрывается для каждого члена, а с несколькими объединениями получаются все комбинации. `${"sm" | "lg"}-${"red" | "blue"}` это "sm-red" | "sm-blue" | "lg-red" | "lg-blue". Очень большие комбинации завершаются ошибкой TS2590.

Что делают Uppercase, Lowercase, Capitalize и Uncapitalize?

Это встроенные типы, которые преобразуют строковые литеральные типы: Uppercase<"id"> это "ID", Lowercase<"ID"> это "id", Capitalize<"name"> это "Name", а Uncapitalize<"Name"> это "name". Они меняют только типы; чтобы изменить строку во время выполнения, по-прежнему вызывайте toUpperCase() и подобные методы.

Проверяют ли шаблонные литеральные типы строки во время выполнения?

Нет. Как и любые типы TypeScript, они стираются, поэтому проверяют только строковые литералы и типизированные значения при компиляции. Строка, пришедшая во время выполнения из JSON или пользовательского ввода, остаётся просто string, пока вы не проверите её собственным кодом.

Почему моя шаблонная строка имеет тип string, а не литеральный тип?

Шаблонное выражение вроде `on${event}` при присваивании переменной расширяется до string. Добавьте as const (`on${event}` as const) или аннотируйте целевой тип, и TypeScript сохранит шаблонный литеральный тип, например "onclick" | "onfocus".

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ