Объединение типов
Чему вы научитесь:
- описывать значение, которое может быть одного из нескольких типов;
- понимать, почему у значения типа
string | numberдоступно только общее для обоих типов; - описывать значение, которого может не быть, и работать с ним через
?.и??; - ограничивать значение фиксированным набором вариантов: литеральными типами и
enum.

Представьте парковку с табличкой «Только машины или мотоциклы». Вы работаете на ней и подходите к месту, не зная, что на нём стоит. Заправить можно и машину, и мотоцикл: бак есть у обоих. А открыть багажник получится только у машины: если на месте мотоцикл, багажника там нет.
Значения в программах бывают такими же. Номер заказа в магазине может быть числом 101, а может быть строкой "A-7". В JavaScript функция примет и то и другое. В TypeScript у параметра есть тип, и одного number здесь мало:
function printId(id: number) {
console.log(id);
}
printId(101);
printId("A-7"); // ✗error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.tsc сообщает, что строку нельзя передать в параметр типа number. Нужен способ сказать «число или строка» — такой же, как табличка на парковке.
Тип «одно или другое»
Объединение типов записывают через вертикальную черту: string | number читается как «строка или число». В переменную или параметр такого типа можно положить значение любого из перечисленных типов:
function printId(id: string | number) {
console.log(id);
}
printId(101); // 101
printId("A-7"); // A-7Давайте разберём пример по строкам:
id: string | numberразрешает параметру принимать и строку, и число.printId(101)передаёт число и печатает101.printId("A-7")передаёт строку и печатаетA-7.
Объединение пишут везде, где нужен тип:
- у переменной: в
let id: string | number = 101;позже можно положить"A-7", аtrueуже нельзя; - у параметра функции, как в примере выше;
- у результата функции, после скобок с параметрами.
Типов в объединении может быть больше двух: string | number | boolean.
Что можно делать со значением
Тут снова пригодится табличка на парковке. Внутри printId TypeScript не знает, что пришло на этот раз: число или строка. Поэтому он разрешает только то, что умеют оба типа:
function printId(id: string | number) {
console.log(id.toString());
console.log(id.toUpperCase()); // ✗
}error TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
Property 'toUpperCase' does not exist on type 'number'.Разберём, почему одна строка прошла, а другая нет:
id.toString()компилируется: методtoStringесть и у строки, и у числа. Это как заправка, которая подходит любому транспорту на парковке.id.toUpperCase()вызывает ошибку, и вторая строка сообщения называет причину: уnumberтакого метода нет. Это багажник, которого у мотоцикла не бывает.
Если бы JavaScript выполнил эту функцию с числом, программа упала бы с TypeError: id.toUpperCase is not a function. TypeScript находит эту ошибку заранее, ещё до запуска.
Пройдите пример по шагам и следите, что подсказка редактора говорит про выделенное имя на каждой строке:
1function printId(id: string | number) {подсказка редактораid: string | number2 const text = id.toString();3 console.log(text);4 console.log(id.toUpperCase());5}6 7printId(101);8printId("A-7");
Параметр id может оказаться и строкой, и числом.
Здесь аналогия расходится с TypeScript в одном. На парковке можно подойти и посмотреть, что стоит на месте, а компилятор видит только табличку — тип, а не само значение. Чтобы вызвать метод одного из типов, значение сначала проверяют, например через typeof. Внутри if (typeof id === "string") TypeScript знает, что id — строка.
Этот вопрос часто задают на собеседовании: «Что можно делать со значением типа A | B?» Ответ: только то, что есть у каждого типа из объединения.
Частая ошибка
Значение типа-объединения передали туда, где ждут один-единственный тип:
function shout(text: string) {
console.log(text.toUpperCase());
}
function printId(id: string | number) {
shout(id); // ✗
}error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
Type 'number' is not assignable to type 'string'.Функция shout принимает только строку, а в id может оказаться число — об этом говорит вторая строка сообщения. Исправить можно общим методом: shout(id.toString()) передаёт строку при любом значении id.
Главное
- Объединение
string | numberзначит «строка или число»: в переменную или параметр такого типа можно передать значение любого из этих типов. - Со значением типа-объединения можно делать только то, что есть у каждого его типа:
toString()можно,toUpperCase()— нельзя. - Значение типа
string | numberнельзя передать туда, где ждут толькоstring. - Компилятор видит тип, а не значение, поэтому такие ошибки он находит ещё до запуска.