Optional chaining (?.) и nullish coalescing
(??) относятся к синтаксическим конструкциям, которые
упрощают работу с потенциально отсутствующими значениями и строгой
проверкой null/undefined.
Optional chaining снижает необходимость ручных проверок вложенных объектов:
const city = user?.address?.city;
Nullish coalescing позволяет задать значение по умолчанию только для
null и undefined, не затрагивая ложные
значения (0, ““, false):
const port = config.port ?? 3000;
В контексте трансформации SWC эти конструкции представляют собой синтаксический сахар, который разворачивается в более ранние эквиваленты JavaScript для обеспечения совместимости с целевыми средами выполнения.
SWC строит AST (Abstract Syntax Tree), в котором optional chaining и nullish coalescing представлены отдельными узлами:
OptionalChainingExpression
NullishCoalescingExpression
Пример исходного кода:
const value = obj?.a?.b ?? defaultValue;
AST концептуально разбивается на:
obj?.a?.b
Ключевая задача трансформации — сохранить порядок вычислений и корректную ленивую семантику.
Optional chaining преобразуется в условные проверки с кешированием промежуточных значений.
Исходный код:
const x = obj?.a?.b;
Типичная трансформация:
const _obj = obj;
const x = _obj == null ? undefined : _obj.a == null ? undefined : _obj.a.b;
Логика трансформации строится на двух принципах:
null и undefined через ==
null
При сложных выражениях SWC обязан предотвращать повторное выполнение:
getObj()?.a
Превращается в:
const _ref = getObj();
const _tmp = _ref == null ? undefined : _ref.a;
Ключевой момент — функция getObj() вызывается ровно один
раз.
obj.method?.()
Трансформируется в:
const _obj = obj.method;
_obj == null ? undefined : _obj.call(obj);
Здесь сохраняется контекст вызова (this), что критично для
корректного поведения методов.
a?.b.c?.d
Разворачивается каскадно:
const _a = a;
const _tmp1 = _a == null ? undefined : _a.b;
const _tmp2 = _tmp1 == null ? undefined : _tmp1.c;
const result = _tmp2 == null ? undefined : _tmp2.d;
SWC стремится минимизировать количество временных переменных, но не нарушать семантику.
const x = a ?? b;
Преобразуется в:
const x = a != null ? a : b;
Используется именно != null, чтобы охватить и
null, и undefined.
Комбинированные выражения требуют строгого соблюдения приоритета:
const x = obj?.value ?? 10;
Результат:
const _obj = obj;
const _val = _obj == null ? undefined : _obj.value;
const x = _val != null ? _val : 10;
Важно различие:
const a = value || 10;
const b = value ?? 10;
SWC не объединяет эти конструкции, так как семантика различается:
|| учитывает falsy значения
?? учитывает только null/undefined
При разборе AST SWC строго соблюдает приоритет операторов:
?.)
.)
??)
Пример:
a?.b ?? c?.d
Разбивается на две независимые цепочки, каждая из которых трансформируется отдельно, после чего применяется coalescing.
getA()?.getB()?.value
SWC гарантирует:
getA() вызывается один раз
getB() вызывается не более одного раза
arr[i++]?.value
Трансформация фиксирует индекс:
const _i = i++;
const _arr = arr[_i];
const _tmp = _arr == null ? undefined : _arr.value;
delete obj?.prop
Превращается в:
const _obj = obj;
_obj == null ? true : delete _obj.prop;
Здесь сохраняется семантика delete как выражения,
возвращающего true при отсутствии объекта.
В связке с TypeScript SWC сначала удаляет типы, затем применяет трансформацию:
const x: number = obj?.value;
После стирания типов:
const x = obj?.value;
Дальнейшая трансформация идентична JavaScript-версии.
В конфигурационном файле .swcrc поддержка включается через
preset-env:
{
"jsc": {
"parser": {
"syntax": "ecmascript"
},
"transform": {
"optimizer": {
"globals": {
"vars": {}
}
}
}
},
"env": {
"targets": {
"ie": "11"
}
}
}
При указании старых таргетов SWC автоматически активирует трансформации optional chaining и nullish coalescing.
SWC анализирует target matrix и минимизирует количество трансформаций.
SWC старается переиспользовать временные идентификаторы:
const _ref = a?.b?.c;
вместо:
const _a = a;
const _b = _a == null ? undefined : _a.b;
const _c = _b == null ? undefined : _b.c;
в некоторых режимах оптимизации может быть свернуто в более компактную форму.
Если выражение простое, SWC может избежать промежуточных переменных:
const x = (a == null ? undefined : a.b);
При трансформации SWC сохраняет сопоставление:
obj?.a?.b
Это позволяет отладчикам восстанавливать оригинальный код при step-through отладке.
В сравнении с Babel:
0 ?? 10
Результат всегда 0, трансформация:
0 != null ? 0 : 10;
arr?.[0]?.value ?? "default"
Разворачивается как последовательность безопасных обращений к индексам и свойствам с последующим nullish fallback.
a?.b ?? c?.d ?? e
Формируется каскад условий, где каждая правая часть вычисляется только при необходимости, сохраняя ленивую модель вычислений.