TypeScript: литеральные типы

Литеральные типы — это возможность в TypeScript, которая представляет конкретные значения, а не обобщённые типы.

Это позволяет ограничить тип переменной конкретными значениями, а не широкими типами, такими как string, number и т. д.

Это обеспечивает более точный контроль типов и делает код более типобезопасным.


SVG-диаграмма: литеральные типы Background Title Literal Types Example General Type General Type var direction: string; Can assign any string "up", "down", "abc"... Arrow Literal Type Literal Type var direction: "up" | "down" | "left" | "right" Can only assign one of these four values Arrow Result Type Safety When assigning an incorrect value Compilation Error! direction = "upup" Lower section: Kinds of Literal Types Kinds of Literal Types String literal String literal "up" | "down" | "left" Restrict to specific strings Number literal Number literal 1 | 2 | 3 | 100 | 200 Restrict to specific numbers Boolean literal Boolean literal true | false true or false Template literal Template literal `on${string}` Dynamically generated Arrow marker

Зачем нужны литеральные типы

В TypeScript стандартные типы, такие как string и number, слишком широкие.

Иногда требуется более точный контроль типов. Например, переменная может принимать только несколько конкретных значений.

Литеральные типы как раз предназначены для решения таких задач.

Пояснение концепции:Литеральные типы — это конкретные значения общих типов (string, number). Например"up"ДаstringПодтип, и единственные значения, которые можно ему присвоить, — это конкретные строки.


Строковые литеральные типы

Строковые литеральные типы ограничивают переменную конкретными строковыми значениями.

Это полезно, когда нужно ограничить переменную так, чтобы она принимала только определённые фиксированные значения.

Пример

// Определить строковый литеральный тип
// direction может быть только одним из четырёх конкретных значений
var direction: "up" | "down" | "left" | "right";

// Правильно: присвойте значение из литерального типа
direction = "up";

// Ошибка: присвойте значение, которого нет в списке
// direction = "upup"; // Ошибка компиляции!

console.log("Направление: " + direction);

// Используйте псевдоним типа для создания переиспользуемого литерального типа
// Определите тип статуса: может быть только одним из трёх фиксированных значений
type Status = "pending" | "active" | "completed";

// Используйте псевдоним типа
var currentStatus: Status = "active";
console.log("Статус: " + currentStatus);

Вывод:

方向: up
Status: active

Сценарии применения:Строковые литералы часто используются для определения значений перечислений, кодов состояния, параметров конфигурации и других сценариев, где значения нужно ограничить конкретными.


Числовые литеральные типы

Числовые литеральные типы ограничивают переменную конкретными числовыми значениями.

Это полезно, когда нужно использовать конкретные числовые значения (например, коды состояния, коды ошибок).

Пример

// Определите тип числового литерала
// code может быть только одним из трёх конкретных значений
var code: 200 | 404 | 500;

// Верно: присвоить 200
code = 200;

// Ошибка: присвойте значение, которого нет в списке
// code = 301; // Ошибка компиляции!

console.log("Код состояния: " + code);

// Используйте числовые литералы для имитации перечисления
// Определите дни недели
type Weekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;

// Можно присваивать только числа от 1 до 7
var today: Weekday = 1;
console.log("Сегодня день недели: " + today);

Объяснение:Числовые литералы можно использовать вместо простых перечислений, особенно когда нужно лишь несколько фиксированных числовых значений.


Логические литеральные типы

Логические литеральные типы ограничивают переменную значениями true или false.

Фактически, тип boolean в TypeScript является псевдонимом для `true | false`.

Пример

// Определяем логический литеральный тип
// isActive может быть только true или false
var isActive: true | false;

// Присвоить true
isActive = true;

// На самом деле, boolean — это union-тип из true | false
// Таким образом, эти два способа записи эквивалентны
var flag: boolean = true;

console.log("Is active: " + isActive);

Примечание:Хотя самого boolean достаточно, когда нужно чётко различать типы true и false, можно использовать true или false в качестве типа.


Литеральные типы объектов

Литеральные типы объектов могут определять структуру объекта и использовать модификатор readonly, чтобы сделать свойства доступными только для чтения.

Это очень полезно, когда нужно создавать неизменяемые объекты.

Пример

// Определить литеральный тип объекта
// Указать структуру объекта и типы свойств
type Point = {
    // Координата x точки
    x: число;
    // Координата y точки
    y: число;
};

// Используйте тип объектного литерала
var p: Point = { x: 10, y: 20 };
console.log("Точка: " + JSON.stringify(p));

// Определите тип объекта только для чтения
// Используйте модификатор readonly, чтобы сделать свойства доступными только для чтения
type ReadonlyPoint = {
    // Координата x только для чтения
    readonly x: number;
    // Координата y только для чтения
    readonly y: number;
};

// Используйте тип объекта только для чтения
var rp: ReadonlyPoint = { x: 1, y: 2 };

// Попытка изменить свойство только для чтения вызовет ошибку
// rp.x = 3; // Ошибка компиляции: свойство только для чтения нельзя изменить

console.log("Точка только для чтения: " + JSON.stringify(rp));

Вывод:

点: {"x":10,"y":20}
只读点: {"x":1,"y":2}

Сценарии применения:Объекты только для чтения часто используются для определения конфигурационных объектов, константных объектов и других данных, которые не должны изменяться.


Литеральные типы и вывод типов

TypeScript автоматически выводит литеральные типы на основе того, как объявлены переменные.

Переменные, объявленные с помощью const, выводятся как конкретные литеральные типы, а не как широкие типы.

Пример

// Объявление и присваивание с помощью var
// TypeScript выведет широкий тип
var name = "Alice";    // Тип: string (потому что var можно переприсвоить)
var age = 25;          // Тип: number
var enabled = true;    // Тип: boolean

console.log("Имя: " + name + ", Возраст: " + age + ", Включено: " + enabled);

// Используйте as const, чтобы создать глубокий литерал, доступный только для чтения
// Массив становится кортежем только для чтения, а значения — литеральными типами
var colors = ["red", "green", "blue"] as const;

// Тип colors становится:
// readonly ["red", "green", "blue"]

// Доступ к элементам массива
console.log("Цвета: " + colors[0]);

// Попытка изменить вызовет ошибку
// colors

Пояснение:as constОн выведет тип как наиболее конкретный литеральный тип и добавит модификатор readonly.


Шаблонные литеральные типы

Шаблонные литеральные типы используют синтаксис шаблонных строк JavaScript для создания новых типов.

Это позволяет нам динамически генерировать строковые типы.

Пример

// Используйте шаблонные литеральные типы для определения имён событий
// `on${string}` представляет любую строку, начинающуюся с "on"
type EventName = `on${string}`;

// Используйте шаблонные литеральные типы для определения имён обработчиков
// Capitalize преобразует первую букву строки в верхний регистр
type Handler = `handle${Capitalize<string>}`;

// Используйте шаблонный литеральный тип
var event: EventName = "onClick";
var handler: Handler = "handleSubmit";

console.log("Событие: " + event);
console.log("Обработчик: " + handler);

// Ошибка: не соответствует формату шаблона
// var e: EventName = "click"; // Ошибка компиляции: не начинается с "on"

Результат выполнения:

事件: onClick
处理器: handleSubmit

Встроенные служебные типы:Типы шаблонных литералов можно использовать со встроенными служебными типами, такими как Uppercase, Lowercase, Capitalize, Uncapitalize, и т. д.


Практическое применение: Redux Action

Литеральные типы имеют широкий спектр применения в реальной разработке.

Ниже показано, как использовать литеральные типы для реализации действий в стиле Redux.

Пример

// Определение типов Action в стиле Redux
// Использование объединённых типов для определения различных типов Action
type Action =
    // Действие типа increment, содержащее числовую полезную нагрузку
    | { type: "increment"; payload: number }
    // Действие типа decrement, содержащее числовую полезную нагрузку
    | { type: "decrement"; payload: number }
    // Действие типа reset, без полезной нагрузки
    | { type: "reset" };

// Функция-редьюсер, которая использует action
function reducer(action: Action): void {
    // TypeScript автоматически выведет конкретный тип action на основе значения action.type
    switch (action.type) {
        case "increment":
            // В этой ветке action выводится как { type: "increment"; payload: number }
            console.log("Инкремент: " + action.payload);
            break;
        case "decrement":
            // В этой ветке action выводится как { type: "decrement"; payload: number }
            console.log("Decrement: " + action.payload);
            break;
        case "reset":
            // В этой ветке action выводится как { type: "reset" }
            console.log("Reset");
            break;
    }
}

// Отправка различных типов действий
reducer({ type: "increment", payload: 5 });
reducer({ type: "reset" });

Результат выполнения:

增加: 5
重置

Размеченное объединение:Этот паттерн называется "размеченным объединением", который является лучшей практикой для обработки нескольких связанных типов в TypeScript.


Примечания

  • Сужение типов:При использовании проверок switch или if TypeScript автоматически сужает литеральные типы
  • Псевдонимы типов:Рекомендуется использовать псевдонимы типов для создания псевдонимов часто используемых литеральных типов
  • вывод const:Переменные, объявленные с const, автоматически выводятся как литеральные типы
  • as const:Используйте as const, когда требуется глубокая неизменяемость

Лучшие практики:Когда нужно ограничить переменную только определёнными значениями, предпочитайте литеральные типы вместо enum.


Итог

Литеральные типы — важная возможность TypeScript для достижения точного контроля типов.

  • Строковые литералы:Ограничены конкретными строками, например"up" | "down"
  • Числовые литералы:Ограничены конкретными числами, например1 | 2 | 3
  • Булевы литералы:Ограничены true или false
  • Объектные литералы:Определяют структуру объекта, можно использовать readonly
  • as const:Создаёт глубокие readonly литеральные типы
  • Шаблонные литералы:Динамически генерируют типы с помощью шаблонных строк

Продвинутое:Сочетание литеральных типов с шаблоном размеченного объединения обеспечивает мощную типобезопасность.

Другие расширения