or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-babel--plugin-proposal-optional-chaining

Transform optional chaining operators into a series of nil checks

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@babel/plugin-proposal-optional-chaining@7.21.x

To install, run

npx @tessl/cli install tessl/npm-babel--plugin-proposal-optional-chaining@7.21.0

index.mddocs/

@babel/plugin-proposal-optional-chaining

@babel/plugin-proposal-optional-chaining is a Babel plugin that transforms ES2020 optional chaining syntax (obj?.prop, obj?.method(), obj?.[key]) into conditional null/undefined checks compatible with older JavaScript environments. It enables modern optional chaining syntax in applications that need to support legacy browsers or JavaScript environments that don't natively support ES2020 features.

Package Information

  • Package Name: @babel/plugin-proposal-optional-chaining
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install --save-dev @babel/plugin-proposal-optional-chaining

Core Imports

The plugin is designed to be used within Babel configuration:

// babel.config.js
module.exports = {
  plugins: ["@babel/plugin-proposal-optional-chaining"]
};

For programmatic use:

import optionalChainingPlugin from "@babel/plugin-proposal-optional-chaining";

CommonJS:

const optionalChainingPlugin = require("@babel/plugin-proposal-optional-chaining");

Basic Usage

Babel Configuration

// babel.config.js
module.exports = {
  plugins: [
    ["@babel/plugin-proposal-optional-chaining", { loose: false }]
  ]
};

Transformation Examples

Input ES2020 code:

// Optional member access
const name = obj?.user?.name;
const value = obj?.['property-name'];

// Optional method calls
const result = obj?.method?.();
const data = api?.getData?.();

// Chained optional access
const nested = obj?.prop?.method?.()?.result;

// Delete operations
delete obj?.property;

Output (transformed):

// Strict mode output
var _obj, _obj$user;
const name = (_obj = obj) === null || _obj === void 0 ? void 0 : (_obj$user = _obj.user) === null || _obj$user === void 0 ? void 0 : _obj$user.name;
var _obj2;
const value = (_obj2 = obj) === null || _obj2 === void 0 ? void 0 : _obj2['property-name'];

var _obj3, _obj3$method;
const result = (_obj3 = obj) === null || _obj3 === void 0 ? void 0 : (_obj3$method = _obj3.method) === null || _obj3$method === void 0 ? void 0 : _obj3$method.call(_obj3);
var _api, _api$getData;
const data = (_api = api) === null || _api === void 0 ? void 0 : (_api$getData = _api.getData) === null || _api$getData === void 0 ? void 0 : _api$getData.call(_api);

Capabilities

Plugin Factory Function

Creates a Babel plugin that transforms optional chaining expressions.

/**
 * Creates a Babel plugin for transforming optional chaining syntax using declare helper
 * @param api - Babel plugin API object with version checking and assumptions
 * @param options - Plugin configuration options
 * @returns Babel plugin configuration object
 */
export default declare(
  (api: PluginAPI, options: Options) => PluginObject
);

interface Options {
  /** Enable loose transformation mode for smaller output (default: false) */
  loose?: boolean;
}

interface PluginObject {
  name: "proposal-optional-chaining";
  inherits: typeof syntaxOptionalChaining.default;
  visitor: {
    "OptionalCallExpression|OptionalMemberExpression": (
      path: NodePath<OptionalCallExpression | OptionalMemberExpression>
    ) => void;
  };
}

Transform Function

Core transformation logic for converting optional chaining AST nodes (re-exported from internal module).

/**
 * Transforms optional chaining AST nodes into conditional expressions
 * @param path - NodePath for the optional chaining expression to transform
 * @param options - Transformation behavior options with pureGetters and noDocumentAll flags
 */
export function transform(
  path: NodePath<OptionalCallExpression | OptionalMemberExpression>,
  { pureGetters, noDocumentAll }: { pureGetters: boolean; noDocumentAll: boolean }
): void;

Configuration Options

Basic Options

{
  "plugins": [
    ["@babel/plugin-proposal-optional-chaining", {
      "loose": false  // Use strict null checks (default)
    }]
  ]
}

Loose Mode

Enables simplified transformation for smaller output:

{
  "plugins": [
    ["@babel/plugin-proposal-optional-chaining", {
      "loose": true  // Use simplified null checks
    }]
  ]
}

Loose mode output:

// Input: obj?.prop
// Loose output: obj == null ? void 0 : obj.prop
// Strict output: obj === null || obj === void 0 ? void 0 : obj.prop

Babel Assumptions Integration

The plugin integrates with Babel's assumption system:

// babel.config.js
module.exports = {
  assumptions: {
    "pureGetters": true,     // Assume getters have no side effects
    "noDocumentAll": true    // Use simplified null checks
  },
  plugins: ["@babel/plugin-proposal-optional-chaining"]
};

Transformation Features

Supported Syntax Patterns

  1. Optional Member Access: obj?.prop, obj?.['key']
  2. Optional Method Calls: obj?.method(), obj?.method?.()
  3. Chained Optional Access: obj?.prop?.method?.()
  4. Delete Operations: delete obj?.prop
  5. Complex Expressions: (obj?.prop as any)?.method?.()

Context Preservation

The plugin ensures proper this context for method calls:

// Input
obj?.method?.();

// Output (strict mode)
var _obj, _obj$method;
(_obj = obj) === null || _obj === void 0 ? void 0 : (_obj$method = _obj.method) === null || _obj$method === void 0 ? void 0 : _obj$method.call(_obj);

Side Effect Handling

Prevents duplicate evaluation of expressions with side effects:

// Input
getObject()?.property?.method?.();

// Output - memoizes getObject() call
var _getObject, _getObject$property, _getObject$property$m;
(_getObject = getObject()) === null || _getObject === void 0 ? void 0 : (_getObject$property = _getObject.property) === null || _getObject$property === void 0 ? void 0 : (_getObject$property$m = _getObject$property.method) === null || _getObject$property$m === void 0 ? void 0 : _getObject$property$m.call(_getObject$property);

Optimization Modes

Pure Getters Mode

When pureGetters assumption is enabled, the plugin optimizes member access:

// Input: obj?.prop?.method?.()
// With pureGetters: simpler output avoiding Function.call when possible
// Without pureGetters: always uses Function.call for method context

No Document.all Mode

When noDocumentAll assumption is enabled, uses simplified null checks:

// Input: obj?.prop
// With noDocumentAll: obj != null ? obj.prop : void 0
// Without noDocumentAll: obj !== null && obj !== void 0 ? obj.prop : void 0

Types

// Core types from @babel/helper-plugin-utils and @babel/core
function declare<State = {}, Option = {}>(
  builder: (
    api: PluginAPI,
    options: Option,
    dirname: string,
  ) => PluginObject<State & PluginPass>
): (api: PluginAPI, options: Option, dirname: string) => PluginObject<State & PluginPass>;

interface PluginAPI {
  assertVersion(version: number | string): void;
  assumption(name: string): boolean | undefined;
  version: string;
  targets(): any;
  env: any;
  caller(): any;
  types: typeof t;
  template: any;
  parse: any;
  transform: any;
  traverse: any;
}

interface PluginObject<State = {}> {
  name?: string;
  inherits?: any;
  visitor?: any;
  pre?: (state: State) => void;
  post?: (state: State) => void;
  manipulateOptions?: any;
}

interface PluginPass {
  key?: string;
  file: any;
  opts: any;
}

interface NodePath<T = any> {
  node: T;
  scope: Scope;
  parentPath: NodePath;
  isOptionalMemberExpression(): boolean;
  isOptionalCallExpression(): boolean;
  isPattern(): boolean;
  get(key: string): NodePath;
  replaceWith(node: any): void;
  findParent(callback: (path: NodePath) => boolean): NodePath | null;
}

// AST Node types
interface OptionalMemberExpression {
  type: "OptionalMemberExpression";
  object: Expression;
  property: Expression;
  computed: boolean;
  optional: boolean;
}

interface OptionalCallExpression {
  type: "OptionalCallExpression";
  callee: Expression;
  arguments: Expression[];
  optional: boolean;
}

interface Expression {
  type: string;
}

interface Scope {
  isStatic(node: any): boolean;
  maybeGenerateMemoised(node: any): any;
  path: NodePath;
}

Error Handling

The plugin handles various edge cases:

  • Transparent Expression Wrappers: TypeScript type assertions, parentheses
  • Function Parameter Defaults: Wraps expressions to ensure correct scope
  • Complex Member Chains: Proper memoization to avoid side effects
  • Context Binding: Maintains proper this context for method calls

Dependencies

Runtime Dependencies

  • @babel/helper-plugin-utils: Core plugin utilities
  • @babel/helper-skip-transparent-expression-wrappers: Expression wrapper handling
  • @babel/plugin-syntax-optional-chaining: Syntax parsing support

Peer Dependencies

  • @babel/core: Babel core library (^7.0.0-0)

The plugin requires Node.js >= 6.9.0 and integrates with the Babel 7+ ecosystem.