Опциональный оператор цепочки .?
возвращает значение свойства объекта, если объект доступен, и undefined
в противном случае. Он похож на стандартный оператор цепочки .
, с дополнительной проверкой, определен ли объект (т.е. не nullish).
Необязательный оператор цепочки позволяет писать краткие и безопасные цепочки связанных объектов, когда некоторые из этих объектов могут быть null
или undefined
. До введения опциональной цепочки в ES2020 оператор &&
часто использовался для проверки наличия объекта (obj && obj.value
).
Часто можно упростить существующие проверки с помощью необязательного шаблона chaining:
- и т.д.
Однако есть некоторые случаи, когда рефакторинг в опциональную цепочку может привести к ошибкам:
- Опциональная цепочка замыкается для значений nullish, но не для других ложных значений
- Необязательная цепочка изменяет результат для null на undefined
- Необязательное построение цепочек может повлиять на количество вызовов с побочными эффектами.
- TypeScript не поддерживает необязательную цепочку типа ‘void’
- Старые браузеры и движки JavaScript не поддерживают необязательное построение цепочек
Опциональная цепочка замыкается для значений nullish, но не для других ложных значений
Когда a && a.b
заменяется на a?.b
, выполнение для типов, которые могут иметь ложные значения, изменяется. Это означает, что значение результата и тип выражения могут быть разными при необязательной цепочке.
В следующем фрагменте показаны некоторые примеры:
function test(value) {
console.log(`${value && value.length}, ${value?.length}`);
}
test(undefined); // undefined, undefined
test(null); // null, undefined
test(true); // undefined, undefined
test(false); // false, undefined
test(1); // undefined, undefined
test(0); // 0, undefined
test({}); // undefined, undefined
test([]); // 0, 0
test({ length: "a" }); // a, a
test(''); // , 0
test(NaN); // NaN, undefined
Пустая строка, которая является falsy, но не nullish, может быть особенно проблематичной. Вот пример того, как введение необязательной цепочки может привести к проблемам:
// without optional chaining
if (s && s.length === 0) {
// not called for the empty string
// (e.g., legacy code that works this way)
}
// with optional chaining
if (s?.length === 0) {
// called for the empty string
// (potentially introducing undesired behavior)
}
Необязательная цепочка изменяет результат для null на undefined
При вызове a?.b
с null результатом будет undefined
. Однако при вызове a && a.b
результатом будет null
.
Необязательное построение цепочек может повлиять на количество вызовов с побочными эффектами.
Например, рассмотрим изменение
f() && f().a;
на
f()?.a;
При использовании &&
, f
вызывается один или два раза. Однако при использовании опциональной цепочки f
вызывается только один раз. Если f
имеет побочный эффект, то этот побочный эффект будет вызван другое количество раз, что потенциально может изменить поведение. Такое поведение применимо не только к вызовам функций и методов, но и к геттерам, которые потенциально могут иметь побочные эффекты.
TypeScript не поддерживает необязательную цепочку типа ‘void’
TypeScript не поддерживает необязательную цепочку для void
, хотя соответствующий код JavaScript будет работать.
type Input = void | {
property: string
};
function f(input: Input) {
// this works:
console.log(input && input.property);
// this breaks because void is not undefined in TypeScript:
console.log(input?.property);
}
Старые браузеры и движки JavaScript не поддерживают необязательное построение цепочек
Необязательная цепочка — это функция ES2020. Она поддерживается во всех современных браузерах и Node 14+, но для старых браузеров и версий Node может потребоваться транспонирование (совместимость).