Шаблонные литералы получили незаслуженно мало внимания. Хотя это мощнейший инструмент, который полезен при разработке различного вида кастомизируемых компонентов. Используя и комбинируя шаблоны, можно создавать коллекции строковых типов всего за одну строку.
Предлагаю обсудить эту тему в стиле самих шаблонных литералов — кратко и емко.
Как создать шаблонный литерал
Синтаксис мало чем отличается от привычного нам по JS, только вместо какой-нибудь переменной, мы присваиваем шаблон некоему type
type IPhone = `iPhone ${number}`;
let iphone17: IPhone = 'iPhone 17'; // ok
let pixelPhone: IPhone = 'Pixel '; // ошибка
let iphoneSE: IPhone = 'iPhone SE'; // ошибка
Причем обратите внимание, что TS учитывает тип значения которое является переменной частью строки.
И что со всем этим делать?
Чаще всего это пригождается, когда нужно скомбинировать несколько юнион-типов, чтобы получить наборы допустимых строк
type Color = "red" | "green" | "blue";
type Size = "small" | "medium" | "large";
type ButtonType = `${Color}-${Size}`;
// type ButtonType = "red-small" | "red-medium" | "red-large" |
// "green-small" | "green-medium" | "green-large" |
// "blue-small" | "blue-medium" | "blue-large"
Или, например для позиции какого-нибудь тултипа
type Vertical = "top" | "bottom";
type Horizontal = "left" | "right";
type Position = `${Vertical}-${Horizontal}`;
// "top-left" | "top-right" | "bottom-left" | "bottom-right"
Также в TS есть встроенные ютилити-тайпы
Uppercase<StringType>Lowercase<StringType>Capitalize<StringType>Uncapitalize<StringType>
Применяя их вместе с шаблонными литералами и юнион-типами, можно делать такие вещи:
type Route = "home" | "about" | "contact";
type ApiEndpoint = `api/${Uppercase<Route>}`;
// "api/HOME" | "api/ABOUT" | "api/CONTACT"
type CssClass = `btn-${Lowercase<Route>}`;
// "btn-home" | "btn-about" | "btn-contact"
Еще несколько юз-кейсов
1. Типизируем API событий
type HttpMethod = "get" | "post" | "put" | "delete";
type ApiVersion = "v1" | "v2";
type ApiRoute = `/${ApiVersion}/${Uppercase<HttpMethod>}`;
// "/v1/GET" | "/v1/POST" | "/v1/PUT" | "/v1/DELETE" |
// "/v2/GET" | "/v2/POST" | "/v2/PUT" | "/v2/DELETE"
2. Создаем CSS-in-JS типы
type CssUnit = "px" | "rem" | "em" | "%";
type CssProperty = "margin" | "padding" | "width" | "height";
type CssValue<K extends CssProperty> =
K extends "width" | "height"
? `${number}${CssUnit}`
: `${number}${CssUnit}` | "auto";
type Styles = {
[P in CssProperty as `$${P}`]: CssValue<P>;
};
// { $margin: ..., $padding: ..., $width: ..., $height: ... }
3. Валидируем пути
type Path = `/${string}`;
type FilePath = `${Path}.${"ts" | "js" | "json"}`;
function readFile(path: FilePath) {
// implementation
}
readFile("/src/index.ts"); // ok
readFile("index.js"); // ошибка, т.к. не начинается со слеша
readFile("/config.txt"); // ошибка, т.к. не то расширение
Итого
Используем шаблонные литералы
1. Для валидации строковых форматов
2. Автоматизации генерации наборов строковых типов
Используем с умом и не переусложняем. Желаю успехов!







