JS Refactoring Gotchas: 5 способов, как преобразование к опциональной цепочке может сломать ваш код

Опциональный оператор цепочки .? возвращает значение свойства объекта, если объект доступен, и undefined в противном случае. Он похож на стандартный оператор цепочки ., с дополнительной проверкой, определен ли объект (т.е. не nullish).

Необязательный оператор цепочки позволяет писать краткие и безопасные цепочки связанных объектов, когда некоторые из этих объектов могут быть null или undefined. До введения опциональной цепочки в ES2020 оператор && часто использовался для проверки наличия объекта (obj && obj.value).

Часто можно упростить существующие проверки с помощью необязательного шаблона chaining:

  • и т.д.

Однако есть некоторые случаи, когда рефакторинг в опциональную цепочку может привести к ошибкам:

Опциональная цепочка замыкается для значений 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 может потребоваться транспонирование (совместимость).

Оцените статью
Procodings.ru
Добавить комментарий