null и undefined в объединениях
Вернёмся на парковку. Теперь на табличке написано «Машина — или место свободно». Прежде чем открыть багажник, нужно убедиться, что машина вообще стоит на месте: на пустом месте открывать нечего.
В программах тоже бывает, что значения нет. Функция ищет город пользователя и не всегда его находит. В JavaScript в таком случае часто возвращают null. В строгом режиме null — отдельный тип и в string не помещается. Поэтому TypeScript нужно сказать прямо: результат — строка или null.
function findCity(userId: number): string | null {
if (userId === 1) {
return "Kazan";
}
return null;
}
const city = findCity(2);
console.log(city.toUpperCase()); // ✗error TS18047: 'city' is possibly 'null'.Давайте разберём пример по строкам:
: string | nullпосле скобок с параметрами говорит, что функция вернёт строку илиnull.- Для пользователя
1функция возвращает"Kazan", для остальных —null. const city = findCity(2)получает типstring | null: TypeScript берёт его из объявления функции.city.toUpperCase()вызывает ошибку: tsc сообщает, что вcityможет оказатьсяnull.
Если бы этот JavaScript выполнился, программа упала бы с TypeError: Cannot read properties of null (reading 'toUpperCase'). TypeScript показывает эту ошибку до запуска.
Объединение с null
Запись string | null — обычное объединение типов. Только второй вариант в нём означает «значения нет». Это табличка «машина или пусто»: у значения такого типа нельзя вызвать метод строки, пока не исключён null.
С undefined всё устроено так же. Тип string | undefined значит «строка, а может, ничего не задали».
Эту проверку включает флаг strictNullChecks, который входит в строгий режим strict. Без него null и undefined помещаются в любой тип, tsc молчит, а ошибка снова возникает только при запуске.
Этот вопрос часто задают на собеседовании: «Что даёт strictNullChecks?» Ответ: null и undefined перестают подходить к другим типам, и компилятор требует учесть их явно.
Операторы ?. и ?? с типами
Операторы ?. и ?? из JavaScript работают и в TypeScript. Компилятор понимает, что они делают, и учитывает это в типе результата:
function findCity(userId: number): string | null {
if (userId === 1) {
return "Kazan";
}
return null;
}
const city = findCity(2);
const upper = city?.toUpperCase();
const shown = city ?? "—";
console.log(upper, shown); // undefined —Наведите курсор на upper и shown в редакторе — подсказка покажет их типы. Разберём каждый оператор.
Оператор ?.
city?.toUpperCase() вызывает метод, только если в city не null и не undefined. Иначе всё выражение даёт undefined. Поэтому у upper тип string | undefined: строка, если город нашёлся, и undefined, если нет. Здесь город не нашёлся, и программа печатает undefined.
Оператор ??
city ?? "—" берёт правую часть, когда слева null или undefined. В любом случае получается строка: либо сам город, либо "—". Поэтому у shown тип string, и null из него исчез. Программа печатает —.
Частая ошибка
Результат ?. считают обычной строкой:
function findCity(userId: number): string | null {
if (userId === 1) {
return "Kazan";
}
return null;
}
const city = findCity(1);
const upper: string = city?.toUpperCase(); // ✗error TS2322: Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.Оператор ?. не убирает «пустой» вариант, а меняет null на undefined. Вторая строка сообщения так и говорит: undefined в string не помещается. Неважно, что findCity(1) на деле вернёт строку: компилятор смотрит на тип. Чтобы получилась строка, добавьте значение по умолчанию через ??:
function findCity(userId: number): string | null {
if (userId === 1) {
return "Kazan";
}
return null;
}
const city = findCity(1);
const upper: string = city?.toUpperCase() ?? "—";
console.log(upper); // KAZANГлавное
- Тип
string | nullилиstring | undefinedчестно говорит, что значения может не быть. - Пока
nullне исключён, метод строки на таком значении вызвать нельзя: tsc сообщает'city' is possibly 'null'. - Эту проверку включает
strictNullChecksиз строгого режима. city?.toUpperCase()даётstring | undefined, аcity ?? "—"—string.