The @babel/plugin-transform-object-rest-spread plugin transforms ECMAScript object rest and spread syntax into ES5-compatible code. It enables developers to use modern object destructuring and spread operations while maintaining compatibility with older JavaScript environments.
npm install --save-dev @babel/plugin-transform-object-rest-spreadThis is a Babel plugin, so it's typically configured in your Babel configuration:
// babel.config.js
module.exports = {
plugins: [
"@babel/plugin-transform-object-rest-spread"
]
};With options:
// babel.config.js
module.exports = {
plugins: [
["@babel/plugin-transform-object-rest-spread", {
useBuiltIns: true,
loose: false
}]
]
};For programmatic usage with Babel's API:
import transformObjectRestSpread from "@babel/plugin-transform-object-rest-spread";
import { transform } from "@babel/core";
const result = transform(code, {
plugins: [transformObjectRestSpread]
});Once configured, the plugin automatically transforms object rest and spread syntax in your code:
// Input: Object rest in destructuring
const { a, b, ...rest } = obj;
// Input: Object spread in object literals
const newObj = { ...obj1, prop: 'value', ...obj2 };
// Input: Object rest in function parameters
function fn({ param1, ...restParams }) {
// Function body
}
// All of the above are transformed to ES5-compatible codeThe plugin operates as a Babel transformation plugin that:
objectWithoutProperties and objectSpread2The main plugin export function that integrates with Babel's transformation pipeline.
import { declare } from "@babel/helper-plugin-utils";
import type { PluginPass } from "@babel/core";
/**
* Main plugin export - default export from the package
* This is a Babel plugin factory function created using declare()
* @param api - Babel API object
* @param opts - Plugin options
* @returns Babel plugin configuration object
*/
export default declare((api: any, opts: Options) => {
api.assertVersion(7);
// Plugin implementation returns PluginVisitor object
});
interface Options {
/** Whether to use built-in Object.assign instead of helper functions (default: auto-detected based on targets) */
useBuiltIns?: boolean;
/** Enable loose mode for more compact but less spec-compliant output (default: false) */
loose?: boolean;
}
/**
* Plugin visitor interface - returned by the main plugin function
*/
interface PluginVisitor {
name: "transform-object-rest-spread";
manipulateOptions?: (opts: any, parser: any) => void;
visitor: {
Function(path: NodePath<t.Function>): void;
VariableDeclarator(path: NodePath<t.VariableDeclarator>, file: PluginPass): void;
ExportNamedDeclaration(path: NodePath<t.ExportNamedDeclaration>): void;
CatchClause(path: NodePath<t.CatchClause>): void;
AssignmentExpression(path: NodePath<t.AssignmentExpression>, file: PluginPass): void;
ForXStatement(path: NodePath<t.ForXStatement>): void;
ArrayPattern(path: NodePath<t.ArrayPattern>): void;
ObjectExpression(path: NodePath<t.ObjectExpression>, file: PluginPass): void;
};
}The plugin handles several distinct patterns of object rest and spread syntax:
Transforms object rest patterns in variable declarations:
// Input
const { a, b, ...rest } = obj;
// Output (conceptual - actual output uses helper functions)
const a = obj.a;
const b = obj.b;
const rest = objectWithoutProperties(obj, ["a", "b"]);Transforms object spread syntax in object literals:
// Input
const result = { ...obj1, prop: 'value', ...obj2 };
// Output (conceptual - actual output uses helper functions)
const result = objectSpread2({}, obj1, { prop: 'value' }, obj2);Transforms object rest patterns in function parameter lists:
// Input
function fn({ param1, ...restParams }) {
// Function body
}
// Output (simplified - actual transformation is more complex)
function fn(_ref) {
const param1 = _ref.param1;
const restParams = objectWithoutProperties(_ref, ["param1"]);
// Function body
}Transforms object rest patterns in assignment contexts:
// Input
({ a, ...rest } = obj);
// Output (conceptual)
var _obj = obj;
a = _obj.a;
rest = objectWithoutProperties(_obj, ["a"]);Transforms object rest patterns in for-in/for-of loops and catch clauses:
// Input: for-of with object rest
for (const { key, ...rest } of items) {
// Loop body
}
// Input: catch clause with object rest
try {
// Code
} catch ({ message, ...errorDetails }) {
// Error handling
}Controls whether to use native Object.assign or Babel helper functions:
interface UseBuiltInsOption {
/**
* When true, uses native Object.assign for object spread operations
* When false, uses Babel's _extends helper function
* When undefined, auto-detects based on compilation targets
*/
useBuiltIns?: boolean;
}Usage Example:
// With useBuiltIns: true
const result = Object.assign({}, obj1, obj2);
// With useBuiltIns: false
const result = _extends({}, obj1, obj2);Controls whether to use loose mode for more compact output:
interface LooseOption {
/**
* When true, generates more compact but less spec-compliant code
* Affects symbol handling and property enumeration behavior
* Default: false
*/
loose?: boolean;
}Usage Example:
// With loose: false (strict mode)
// Generates more comprehensive checks for symbols and getters
// With loose: true
// Generates simpler, faster code with fewer edge case checksWhile not directly exposed as public API, the plugin uses several internal helper functions that are worth understanding:
Utility function to determine if the right-hand side of an assignment should be stored in a temporary variable to avoid duplication:
import type { types as t } from "@babel/core";
/**
* Utility function exported from shouldStoreRHSInTemporaryVariable.ts
* Determines if RHS should be stored in temporary variable to avoid duplication
* @param node - AST node representing the left-hand side pattern
* @returns Whether to store RHS in temporary variable
*/
export default function shouldStoreRHSInTemporaryVariable(
node: t.LVal | t.PatternLike
): boolean;Browser compatibility information for Object.assign feature detection:
/**
* Browser compatibility data exported from compat-data.ts
* Contains minimum version information for Object.assign support
*/
export default {
"Object.assign": {
chrome: "49",
opera: "36",
edge: "13",
firefox: "36",
safari: "10",
node: "6",
deno: "1",
ios: "10",
samsung: "5",
opera_mobile: "36",
electron: "0.37"
}
};
interface CompatData {
"Object.assign": {
[browser: string]: string;
};
}The plugin uses several internal functions that transform AST nodes. While not part of the public API, understanding these functions helps explain the plugin's behavior:
/**
* Internal utility functions used by the plugin (not exported)
*/
/**
* Checks if a node contains object rest elements
* @param path - AST node path to check
* @returns true if the node contains object rest patterns
*/
function hasObjectRestElement(
path: NodePath<t.LVal | t.PatternLike | t.TSParameterProperty>
): boolean;
/**
* Checks if an object expression contains spread elements
* @param node - Object expression node to check
* @returns true if the object contains spread syntax
*/
function hasSpread(node: t.ObjectExpression): boolean;
/**
* Extracts all keys from an object pattern for rest transformation
* @param node - Object pattern to extract keys from
* @returns Object containing keys array and metadata
*/
function extractNormalizedKeys(node: t.ObjectPattern): {
keys: t.Expression[];
allPrimitives: boolean;
hasTemplateLiteral: boolean;
};
/**
* Creates the object rest transformation call expression
* @param path - Object pattern path
* @param file - Plugin pass instance
* @param objRef - Reference to the source object
* @returns Tuple containing declarators, argument, and call expression
*/
function createObjectRest(
path: NodePath<t.ObjectPattern>,
file: PluginPass,
objRef: t.Identifier | t.MemberExpression
): [t.VariableDeclarator[], t.AssignmentExpression["left"], t.CallExpression];
/**
* Gets the appropriate helper function for object spread operations
* @param file - Plugin pass instance
* @returns Helper function reference (Object.assign or _extends)
*/
function getExtendsHelper(file: PluginPass): t.MemberExpression | t.Identifier;The plugin requires several Babel types and utilities:
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";
import type { PluginPass, NodePath, Scope } from "@babel/core";
import { convertFunctionParams } from "@babel/plugin-transform-parameters";
import { isRequired } from "@babel/helper-compilation-targets";
import { unshiftForXStatementBody } from "@babel/plugin-transform-destructuring";The plugin integrates with Babel's ecosystem through several mechanisms:
The plugin respects the following Babel assumptions for optimization:
ignoreFunctionLength: Whether to ignore function.length when transforming parametersobjectRestNoSymbols: Whether object rest excludes symbol propertiespureGetters: Whether getters are side-effect free (enables optimizations)setSpreadProperties: Whether to use simple property assignment for spreadThe plugin requires Babel 7.0.0 or higher and handles version-specific differences. The plugin automatically adapts its behavior based on the Babel version being used, with enhanced features available in Babel 8+.
The plugin has the following dependencies:
/**
* Package dependencies as specified in package.json
*/
interface Dependencies {
"@babel/helper-compilation-targets": string;
"@babel/helper-plugin-utils": string;
"@babel/plugin-transform-destructuring": string;
"@babel/plugin-transform-parameters": string;
"@babel/traverse": string;
}
/**
* Peer dependencies - must be installed by consuming applications
*/
interface PeerDependencies {
"@babel/core": "^7.0.0-0";
}
/**
* Engine requirements
*/
interface Engines {
node: ">=6.9.0";
}The plugin validates configuration options and throws descriptive errors for invalid settings:
// Invalid loose option
{
plugins: [
["@babel/plugin-transform-object-rest-spread", { loose: "invalid" }]
]
}
// Throws: ".loose must be a boolean, or undefined"