or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

babel-plugin-transform-es2015-typeof-symbol

A Babel plugin that transforms typeof expressions to properly handle ES6 symbols. This plugin provides ES2015 compatibility for the typeof operator when working with symbols by wrapping typeof expressions with a method that replicates native behavior, returning "symbol" for symbols and preserving standard typeof behavior for all other types.

Package Information

  • Package Name: babel-plugin-transform-es2015-typeof-symbol
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install --save-dev babel-plugin-transform-es2015-typeof-symbol

Core Imports

// The plugin is imported by Babel automatically when configured
export default function ({ types: t }) { ... }

Basic Usage

Via .babelrc (Recommended)

{
  "plugins": ["transform-es2015-typeof-symbol"]
}

Via CLI

babel --plugins transform-es2015-typeof-symbol script.js

Via Node API

require("babel-core").transform("code", {
  plugins: ["transform-es2015-typeof-symbol"]
});

Transformation Example

Input:

var s = Symbol("s");
assert.ok(typeof s === "symbol");
assert.equal(typeof s, "symbol");
typeof s === "string";

Output:

var s = Symbol("s");
assert.ok((typeof s === "undefined" ? "undefined" : babelHelpers.typeof(s)) === "symbol");
assert.equal(typeof s === "undefined" ? "undefined" : babelHelpers.typeof(s), "symbol");
typeof s === "string";

Capabilities

Plugin Factory Function

The main export is a Babel plugin factory function that returns a plugin configuration object.

/**
 * Main plugin factory function for babel-plugin-transform-es2015-typeof-symbol
 * @param {Object} babel - Babel instance with types utility
 * @param {Object} babel.types - Babel types utility object (aliased as t)
 * @returns {Object} Babel plugin configuration object with visitor methods
 */
export default function ({ types: t }) {
  const IGNORE = Symbol();
  
  return {
    visitor: {
      Scope({ scope }) { /* ... */ },
      UnaryExpression(path) { /* ... */ }
    }
  };
}

Plugin Configuration Object

The returned plugin object contains visitor methods for AST transformation.

interface PluginConfig {
  visitor: {
    /**
     * Handles Symbol identifier conflicts by renaming local Symbol bindings
     * @param {Object} path - Babel path object with scope property
     */
    Scope: (path: { scope: Object }) => void;
    
    /**
     * Transforms typeof expressions to handle symbols correctly
     * @param {Object} path - Babel path object for UnaryExpression nodes
     */
    UnaryExpression: (path: Object) => void;
  };
}

Visitor Methods

Scope Visitor

Handles Symbol identifier conflicts by renaming local Symbol bindings.

/**
 * Processes scope to avoid Symbol identifier conflicts
 * @param {Object} path - Babel path object with scope property
 * @param {Object} path.scope - Babel scope object
 */
Scope({ scope }) {
  if (!scope.getBinding("Symbol")) {
    return;
  }
  scope.rename("Symbol");
}

UnaryExpression Visitor

Transforms typeof expressions to handle symbols correctly.

/**
 * Transforms typeof expressions to handle symbols properly
 * @param {Object} path - Babel path object for UnaryExpression nodes
 * @param {Object} path.node - The AST node being processed
 * @param {Object} path.parent - The parent AST node
 */
UnaryExpression(path) {
  const { node, parent } = path;
  if (node[IGNORE]) return;
  if (path.find((path) => path.node && !!path.node._generated)) return;

  // Optimization: skip transformation for typeof comparisons with non-symbol/object literals
  if (path.parentPath.isBinaryExpression() && 
      t.EQUALITY_BINARY_OPERATORS.indexOf(parent.operator) >= 0) {
    const opposite = path.getOpposite();
    if (opposite.isLiteral() && opposite.node.value !== "symbol" && 
        opposite.node.value !== "object") {
      return;
    }
  }

  if (node.operator === "typeof") {
    const call = t.callExpression(this.addHelper("typeof"), [node.argument]);
    if (path.get("argument").isIdentifier()) {
      // Handle identifiers with conditional expression for undefined variables
      const undefLiteral = t.stringLiteral("undefined");
      const unary = t.unaryExpression("typeof", node.argument);
      unary[IGNORE] = true;
      path.replaceWith(t.conditionalExpression(
        t.binaryExpression("===", unary, undefLiteral),
        undefLiteral,
        call
      ));
    } else {
      // Handle expressions directly with helper
      path.replaceWith(call);
    }
  }
}

Plugin Behavior

Transformation Logic

  1. Symbol Scope Handling: Detects and renames local Symbol bindings to avoid conflicts using scope.rename("Symbol")
  2. Double-Processing Prevention: Uses an internal IGNORE Symbol to mark already processed nodes and avoid infinite recursion
  3. Generated Code Detection: Skips transformation for already generated code by checking _generated flag
  4. Typeof Detection: Identifies typeof unary expressions in the AST
  5. Optimization: Skips transformation when comparing typeof result to non-symbol/non-object literals (e.g., typeof foo === "string")
  6. Helper Integration: Uses Babel's helper system via this.addHelper("typeof") rather than generating inline code
  7. Conditional Expression Logic: For identifiers, generates conditional expressions to handle undefined variables: typeof x === "undefined" ? "undefined" : helper(x)
  8. Direct Helper Calls: For non-identifier expressions, directly replaces with helper function calls

Performance Optimizations

The plugin includes several optimizations:

  • Static Analysis: Avoids transformation when comparing typeof result to non-symbol/non-object types using t.EQUALITY_BINARY_OPERATORS
  • Binary Expression Optimization: Skips transformation for typeof foo === "string" patterns by checking opposite.node.value
  • Generated Code Detection: Avoids processing already transformed expressions using _generated flag checks
  • Double-Processing Prevention: Uses internal IGNORE Symbol to mark processed unary expressions
  • Conditional Evaluation: Only applies helper for non-undefined identifiers, preserving JavaScript's undefined behavior

Error Handling

  • Preserves JavaScript's undefined variable behavior
  • Maintains proper typeof semantics for all non-symbol types
  • Uses conditional expressions to handle undefined identifiers correctly

Integration

This plugin integrates with the Babel transformation pipeline and:

  • Babel Helper System: Uses this.addHelper("typeof") to inject the appropriate runtime helper function
  • Visitor Pattern: Follows standard Babel plugin visitor patterns with Scope and UnaryExpression visitors
  • AST Manipulation: Uses Babel types (t) for creating conditional expressions, binary expressions, and call expressions
  • Code Generation: Respects Babel's code generation and source mapping through proper AST node creation
  • Plugin Ecosystem: Works with other Babel plugins and presets without conflicts
  • Runtime Dependencies: Depends on babel-runtime for the actual typeof helper implementation

Dependencies

  • babel-runtime: ^6.22.0 (runtime dependency for helper functions)
  • Babel Helper System: Requires Babel's typeof helper to be available during transformation
  • Babel Types: Uses Babel's type system for AST node creation and manipulation

Helper Function Details

The plugin relies on Babel's helper system to provide the actual typeof implementation. The helper function generated by Babel typically looks like:

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" 
  ? function (obj) { return typeof obj; } 
  : function (obj) { 
      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype 
        ? "symbol" 
        : typeof obj; 
    };

This helper function:

  • Detects if Symbol is available and properly supported
  • Returns "symbol" for actual Symbol instances
  • Falls back to standard typeof behavior for all other types
  • Handles Symbol polyfills and edge cases correctly