@babel/plugin-proposal-nullish-coalescing-operator is a Babel plugin that transforms nullish coalescing operator (??) syntax into equivalent conditional expressions compatible with older JavaScript environments. It converts expressions like a ?? b into a !== null && a !== void 0 ? a : b, handling edge cases like the document.all quirk in browsers.
npm install --save-dev @babel/plugin-proposal-nullish-coalescing-operatorThis plugin is used in Babel configuration and not imported directly in code:
// babel.config.js
module.exports = {
plugins: ["@babel/plugin-proposal-nullish-coalescing-operator"]
};With options:
// babel.config.js
module.exports = {
plugins: [
["@babel/plugin-proposal-nullish-coalescing-operator", { loose: true }]
]
};Using Babel assumptions:
// babel.config.js
module.exports = {
plugins: ["@babel/plugin-proposal-nullish-coalescing-operator"],
assumptions: {
noDocumentAll: true
}
};For plugin development or understanding the implementation:
import { declare } from "@babel/helper-plugin-utils";
import syntaxNullishCoalescingOperator from "@babel/plugin-syntax-nullish-coalescing-operator";
import { types as t, template } from "@babel/core";Input code with nullish coalescing operator:
const result = a ?? b;
const nested = obj.prop ?? defaultValue;
const chained = first ?? second ?? third;Output after transformation (strict mode):
const result = a !== null && a !== void 0 ? a : b;
const nested = (_obj$prop = obj.prop) !== null && _obj$prop !== void 0 ? _obj$prop : defaultValue;
const chained = (_first = first) !== null && _first !== void 0 ? _first : (_second = second) !== null && _second !== void 0 ? _second : third;Output after transformation (loose mode):
const result = a != null ? a : b;
const nested = (_obj$prop = obj.prop) != null ? _obj$prop : defaultValue;
const chained = (_first = first) != null ? _first : (_second = second) != null ? _second : third;Main Babel plugin function that provides nullish coalescing operator transformation.
/**
* Babel plugin for transforming nullish coalescing operator
* Uses the declare function from @babel/helper-plugin-utils
*/
const plugin = declare((api: any, options: Options) => {
api.assertVersion(7);
const noDocumentAll = api.assumption("noDocumentAll") ?? options.loose;
return {
name: "proposal-nullish-coalescing-operator",
inherits: syntaxNullishCoalescingOperator.default,
visitor: {
LogicalExpression(path: any): void;
}
};
});
export default plugin;Configuration options for the nullish coalescing operator transformation.
/**
* Plugin configuration options
*/
interface Options {
/**
* Use loose transformation (a != null instead of a !== null && a !== void 0)
* Can also be controlled via noDocumentAll assumption
*/
loose?: boolean;
}
export { Options };
/** Required dependencies for plugin implementation */
declare module \"@babel/helper-plugin-utils\" {
export function declare(builder: (api: any, options: any) => any): any;
}
declare module \"@babel/plugin-syntax-nullish-coalescing-operator\" {
const plugin: { default: any };
export default plugin;
}
/** Babel visitor interface for LogicalExpression nodes */
interface PluginVisitor {
LogicalExpression(path: {
node: {
operator: string;
left: any;
right: any;
};
scope: {
isStatic(node: any): boolean;
path: { isPattern(): boolean };
generateUidIdentifierBasedOnNode(node: any): any;
push(options: { id: any }): void;
buildUndefinedNode(): any;
};
replaceWith(node: any): void;
}): void;
}The plugin supports two transformation modes:
Strict Mode (default):
a !== null && a !== void 0document.all edge case correctly (document.all == null but is not nullish)Loose Mode:
a != nullloose: true option or noDocumentAll: true assumptionapi.assumption("noDocumentAll") ?? loose to determine modeThe plugin includes sophisticated scope analysis to avoid variable name conflicts:
scope.isStatic(node.left)), no temporary variable is createdscope.generateUidIdentifierBasedOnNode(node.left) and pushes them to scopescope.path.isPattern()), wraps the expression in an IIFE to ensure proper scoping: (() => ${path.node})()The plugin transforms nullish coalescing expressions using this logic:
LogicalExpression nodes with ?? operatora ?? b with t.conditionalExpression() based on mode:
t.logicalExpression("&&", ...) with separate null and undefined checkst.binaryExpression("!=", ...) with single null checkt.assignmentExpression("=", ref, node.left) for dynamic referencesThe plugin handles nullish coalescing in various contexts:
a ?? bobj.prop ?? defaultfn() ?? backupfunction(x = a ?? b) {}const { prop = a ?? b } = obj(a ?? b) ?? c{
"plugins": ["@babel/plugin-proposal-nullish-coalescing-operator"]
}{
"plugins": [
["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": true }]
]
}{
"plugins": ["@babel/plugin-proposal-nullish-coalescing-operator"],
"assumptions": {
"noDocumentAll": true
}
}This plugin automatically inherits @babel/plugin-syntax-nullish-coalescing-operator and doesn't require it to be explicitly included:
{
"plugins": [
"@babel/plugin-proposal-nullish-coalescing-operator"
]
}Inherited Syntax Plugin: @babel/plugin-syntax-nullish-coalescing-operator is automatically inherited via the inherits property, enabling parsing of the nullish coalescing operator syntax.
The plugin works seamlessly with other Babel plugins and presets. No additional syntax plugins are required.