?. и ??
Проверять undefined через if можно, но когда нужно всего лишь получить значение или замену, кода выходит много:
type Weapon = { name: string; damage: number };
type Hero = { name: string; weapon?: Weapon };
const kira: Hero = { name: "Кира", weapon: { name: "Эльфийский лук", damage: 8 } };
const lena: Hero = { name: "Лена" };
let weaponName = "без оружия";
if (lena.weapon !== undefined) {
weaponName = lena.weapon.name;
}
console.log(weaponName);В JavaScript есть два оператора, которые это сокращают, и TypeScript понимает оба.
Оператор опциональной цепочки (optional chaining) ?. читает свойство, только если значение перед ним существует. lena.weapon?.name означает: если lena.weapon равно undefined или null, результат — undefined; иначе это lena.weapon.name. Ни сбоя, ни ошибки:
type Weapon = { name: string; damage: number };
type Hero = { name: string; weapon?: Weapon };
const kira: Hero = { name: "Кира", weapon: { name: "Эльфийский лук", damage: 8 } };
const lena: Hero = { name: "Лена" };
console.log(kira.weapon?.name);
console.log(lena.weapon?.name);Программа выведет:
Эльфийский лук
undefinedС обычной точкой, lena.weapon.name, TypeScript сообщил бы error TS18048: 'lena.weapon' is possibly 'undefined'. Он знает и то, что результата ?. может не быть: тип lena.weapon?.name — string | undefined.
Оператор нулевого слияния (nullish coalescing) ?? задаёт замену для отсутствующего значения. a ?? b — это a, если только a не null или undefined; тогда это b. Эти два оператора хорошо работают вместе:
console.log(kira.weapon?.name ?? "без оружия");
console.log(lena.weapon?.name ?? "без оружия");Программа выведет:
Эльфийский лук
без оружияТип lena.weapon?.name ?? "без оружия" — просто string: TypeScript знает, что ?? подставил замену вместо отсутствующего значения. Результат можно положить в переменную типа string или вернуть из функции, которая возвращает string. Без ?? return такой функции — ошибка: Type 'string | undefined' is not assignable to type 'string'.
Для замены часто используют и ||. Кроме true и false, a || b работает с любыми значениями: он даёт a, если a считается истинным, и b в остальных случаях. Но 0 и пустая строка "" считаются ложными, поэтому || заменяет и их, а ?? заменяет только null и undefined. С числами эта разница важна:
const arrows = 0;
console.log(arrows || 10);
console.log(arrows ?? 10);Программа выведет:
10
0У героя без стрел 0 стрел, а не 10. Когда вы имеете в виду «если значения нет», используйте ??.
Типичная ошибка: ?. не на своём месте. Его ставят сразу после значения, которого может не быть: lena.weapon?.name, потому что необязательно именно weapon. Запись lena?.weapon.name проверяет lena, которая есть всегда, и TypeScript по-прежнему сообщает 'lena.weapon' is possibly 'undefined'.
Задание
- 1
У Лены нет оружия, поэтому TypeScript сообщает об ошибке в
hero.weapon.name. Исправьте строку с помощью?.и??, чтобы герой без оружия получал строкубез оружия. Тогда программа выведет:Кира: Эльфийский лук Бруно: Боевой топор Лена: без оружия - 2
Добавьте в каждую строку урон оружия. Герой без оружия сражается голыми руками, с уроном
1: снова используйте?.и??. Тогда программа выведет:Кира: Эльфийский лук (урон 8) Бруно: Боевой топор (урон 11) Лена: без оружия (урон 1) - 3
Сложите урон всех героев (снова с
1для голых рук) и выведите сумму после цикла. Тогда программа выведет:Кира: Эльфийский лук (урон 8) Бруно: Боевой топор (урон 11) Лена: без оружия (урон 1) Общий урон: 20