Frontend Development 14 min read

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.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Developing a Babel Plugin to Automatically Add Optional Chaining (?.) and Prevent TypeError

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

JavaScriptBabelplugin developmentCode Transformationoptional-chainingTypeError Prevention
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.