Developing a Babel Plugin to Automatically Add Optional Chaining (?.) and Prevent TypeError
The article explains how to create a custom Babel plugin that automatically transforms risky member and call expressions into optional‑chaining equivalents during bundling, using configurable include/exclude patterns and short‑circuit optimizations, thereby preventing TypeError in large JavaScript codebases while noting a modest bundle‑size increase.
Background
In JavaScript, accessing a property of null or undefined throws a TypeError . ECMAScript 2020 introduced the optional chaining operator ?. , which safely returns undefined when a property does not exist, thus preventing the error.
Project Pain Points
Large codebases contain many direct property accesses such as a.b.c.d , which are potential TypeError sources and costly to maintain.
Manually writing optional chaining ( a?.b?.c?.d ) is verbose and reduces code readability.
Automatically inserting ?. during the build process can solve both problems.
Solution Overview
A custom Babel plugin is created to transform risky member and call expressions into their optional equivalents at bundling time, eliminating TypeError without changing the original source code.
Core Plugin Implementation
import { declare } from '@babel/helper-plugin-utils';
import * as t from '@babel/types';
export default declare((api, options) => {
// Only Babel 7 is supported
api.assertVersion(7);
return {
name: 'babel-plugin-auto-optional-chaining',
visitor: {
'MemberExpression|CallExpression|OptionalMemberExpression|OptionalCallExpression'(path) {
// Skip already processed nodes
if (path.node.extra?.hasAoc) return;
const isMeCe = path.isMemberExpression() || path.isCallExpression();
if (isMeCe && !isValidPath(path, options)) return;
// Property access
if (path.isMemberExpression() || path.isOptionalMemberExpression()) {
const ome = t.OptionalMemberExpression(path.node.object, path.node.property, path.node.computed, true);
if (!shortCircuitOptimized(path, ome)) {
path.replaceWith(ome);
}
}
// Method call
if (path.isCallExpression() || path.isOptionalCallExpression()) {
const oce = t.OptionalCallExpression(path.node.callee, path.node.arguments, false);
if (!shortCircuitOptimized(path, oce)) {
path.replaceWith(oce);
}
}
// Mark as processed
path.node.extra = { ...(path.node.extra || {}), hasAoc: true };
}
}
};
});The plugin recognises MemberExpression and CallExpression nodes, optionally filters them using includes / excludes configuration, and replaces them with OptionalMemberExpression or OptionalCallExpression . It also optimises short‑circuit && expressions.
Configuration
module.exports = {
plugins: [
['babel-plugin-auto-optional-chaining', {
excludes: [
'new .*', // e.g., new a.b() should not be transformed
'process.env.*' // environment variables stay unchanged
],
// includes: [],
// optimizer: false
}]
]
};Three options are supported:
includes – only transform the specified patterns (higher priority than excludes ).
excludes – patterns that should be ignored.
optimizer – set to false to disable &&‑expression optimisation.
Limitations
Automatic insertion of ?. may increase the final bundle size slightly, which could affect page load performance.
Related Plugins
For browsers that do not support optional chaining (e.g., Chrome < 80), the official @babel/plugin-transform-optional-chaining can be used to transpile the code back to a compatible form.
Testing
Test cases are written with babel-plugin-tester . Example input and expected output snippets demonstrate the transformation of regular property accesses, method calls, and &&‑optimised expressions.
// Input
const x = a.b.c.d;
// Output
const x = a?.b?.c?.d;Comprehensive test suites cover edge cases such as bracket notation, unary operators, short‑circuit logic, and exclusion patterns.
Conclusion
The article shows how to build a Babel plugin that automatically adds optional chaining during the build step, effectively preventing TypeError in JavaScript projects and improving code robustness.
References
tc39/proposal-optional-chaining
Optional chaining (?.) on MDN
Babel Plugin Handbook
Babel's optional chaining AST spec
ESTree Specification
eslint/no-unsafe-optional-chaining
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.