Docs

3.8-Rest-Operator

3.8 Rest Operator

Table of Contents

  1. •What Is the Rest Operator
  2. •Rest in Function Parameters
  3. •Rest in Array Destructuring
  4. •Rest in Object Destructuring
  5. •Rest vs Spread
  6. •Common Patterns
  7. •Best Practices

What Is the Rest Operator

The rest operator (...) collects multiple elements into a single array or object. It's the opposite of the spread operator.

Key Concept

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  SPREAD: Expands    [...array] → element1, element2, ...   │
│  REST:   Collects   element1, element2, ... → [...array]   │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                             │
│  function example(first, second, ...rest) {                │
│      // rest collects remaining arguments into an array    │
│  }                                                          │
│                                                             │
│  example(1, 2, 3, 4, 5);                                   │
│  // first = 1                                              │
│  // second = 2                                             │
│  // rest = [3, 4, 5]                                       │
│                                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Where Rest is Used

ContextExamplePurpose
Function parametersfunction fn(...args)Collect all/remaining arguments
Array destructuringconst [a, ...rest] = arrCollect remaining elements
Object destructuringconst {a, ...rest} = objCollect remaining properties

Rest in Function Parameters

Collecting All Arguments

function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum()); // 0

Collecting Remaining Arguments

function greet(greeting, ...names) {
  return names.map((name) => `${greeting}, ${name}!`);
}

console.log(greet('Hello', 'Alice', 'Bob', 'Charlie'));
// ["Hello, Alice!", "Hello, Bob!", "Hello, Charlie!"]

Rest vs arguments Object

// Old way: arguments object (array-like, not a real array)
function oldWay() {
  console.log(arguments); // { '0': 1, '1': 2, '2': 3 }
  console.log(arguments.length); // 3
  // arguments.map() would fail - not a real array!
  return Array.from(arguments).map((x) => x * 2);
}

// New way: rest parameters (real array)
function newWay(...args) {
  console.log(args); // [1, 2, 3]
  return args.map((x) => x * 2); // Works directly!
}

oldWay(1, 2, 3);
newWay(1, 2, 3);

Benefits Over arguments

FeatureargumentsRest parameters
Is a real arrayNoYes
Works in arrow functionsNoYes
Can be namedNoYes
Can use array methodsNo (need conversion)Yes
Works with destructuringNoYes

Rules for Rest Parameters

// āœ… Rest must be last parameter
function valid(a, b, ...rest) {}

// āŒ Rest cannot be followed by other parameters
// function invalid(a, ...rest, b) { }  // SyntaxError

// āŒ Only one rest parameter allowed
// function invalid(...a, ...b) { }     // SyntaxError

// āœ… Rest can be the only parameter
function onlyRest(...args) {}

Rest in Array Destructuring

Basic Usage

const numbers = [1, 2, 3, 4, 5];

const [first, second, ...rest] = numbers;

console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]

Skipping Elements

const values = [1, 2, 3, 4, 5, 6];

// Skip second element
const [a, , ...remaining] = values;
console.log(a); // 1
console.log(remaining); // [3, 4, 5, 6]

With Default Values

const short = [1];

const [first, second = 0, ...rest] = short;
console.log(first); // 1
console.log(second); // 0 (default)
console.log(rest); // [] (empty array)

Practical Example: Head and Tail

const list = [1, 2, 3, 4, 5];

// Common functional programming pattern
const [head, ...tail] = list;
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]

// First and last (with reverse)
const [first, ...middle] = list;
const last = list[list.length - 1];
console.log(first, middle.slice(0, -1), last); // 1, [2, 3, 4], 5

Rest in Object Destructuring

Basic Usage

const user = {
  id: 1,
  name: 'John',
  email: 'john@example.com',
  age: 30,
};

const { name, ...rest } = user;

console.log(name); // 'John'
console.log(rest); // { id: 1, email: 'john@example.com', age: 30 }

Removing Properties

const response = {
  data: { users: [] },
  status: 200,
  headers: { 'content-type': 'application/json' },
  config: { timeout: 5000 },
};

// Extract only what you need
const { data, status, ...meta } = response;

console.log(data); // { users: [] }
console.log(status); // 200
console.log(meta); // { headers: {...}, config: {...} }

Excluding Sensitive Data

const userRecord = {
  id: 1,
  username: 'john_doe',
  password: 'hashed_password',
  email: 'john@example.com',
  role: 'admin',
};

// Remove password before sending to client
const { password, ...safeUser } = userRecord;
console.log(safeUser);
// { id: 1, username: 'john_doe', email: 'john@example.com', role: 'admin' }

With Renaming

const config = {
  apiKey: 'abc123',
  apiSecret: 'secret',
  baseUrl: 'https://api.example.com',
};

const { apiKey: key, ...publicConfig } = config;

console.log(key); // 'abc123'
console.log(publicConfig); // { apiSecret: 'secret', baseUrl: '...' }

Rest vs Spread

The Same Syntax, Opposite Operations

// SPREAD: Expands into individual elements/properties
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // Spread expands arr1
// arr2 = [1, 2, 3, 4, 5]

// REST: Collects into an array/object
const [first, ...rest] = arr2; // Rest collects remaining
// first = 1, rest = [2, 3, 4, 5]

How to Tell the Difference

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  If ... is on the RIGHT side of = (or in function call)    │
│  → It's SPREAD (expanding)                                  │
│                                                             │
│  If ... is on the LEFT side of = (or in function params)   │
│  → It's REST (collecting)                                   │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                             │
│  const copy = [...original];     // SPREAD (right side)    │
│  const [a, ...rest] = array;     // REST (left side)       │
│                                                             │
│  func(...args);                  // SPREAD (in call)       │
│  function func(...args) { }      // REST (in params)       │
│                                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Combined Example

// REST: Collect arguments into array
function process(...items) {
  // SPREAD: Expand array into new array with additions
  return [...items, 'processed'];
}

const input = [1, 2, 3];
// SPREAD: Expand input into function arguments
const result = process(...input); // Same as process(1, 2, 3)

console.log(result); // [1, 2, 3, 'processed']

Common Patterns

1. Variadic Functions

// Function that accepts any number of arguments
function log(level, ...messages) {
  const timestamp = new Date().toISOString();
  messages.forEach((msg) => {
    console.log(`[${timestamp}] [${level}] ${msg}`);
  });
}

log('INFO', 'Server started', 'Port 3000');
log('ERROR', 'Connection failed');

2. Function Wrapping

function withLogging(fn) {
  return function (...args) {
    console.log('Arguments:', args);
    const result = fn(...args);
    console.log('Result:', result);
    return result;
  };
}

const add = (a, b) => a + b;
const loggedAdd = withLogging(add);

loggedAdd(2, 3); // Logs arguments and result

3. Array Manipulation

// Remove first element (immutably)
const removeFirst = ([, ...rest]) => rest;
console.log(removeFirst([1, 2, 3, 4])); // [2, 3, 4]

// Swap first two elements
const swapFirstTwo = ([a, b, ...rest]) => [b, a, ...rest];
console.log(swapFirstTwo([1, 2, 3, 4])); // [2, 1, 3, 4]

// Rotate array
const rotate = ([first, ...rest]) => [...rest, first];
console.log(rotate([1, 2, 3, 4])); // [2, 3, 4, 1]

4. Object Updates

// Remove property immutably
const removeProperty = (obj, prop) => {
  const { [prop]: removed, ...rest } = obj;
  return rest;
};

const user = { name: 'John', age: 30, email: 'john@example.com' };
console.log(removeProperty(user, 'email'));
// { name: 'John', age: 30 }

// Pick specific properties
const pick = (obj, ...keys) => {
  return keys.reduce((result, key) => {
    if (key in obj) result[key] = obj[key];
    return result;
  }, {});
};

console.log(pick(user, 'name', 'email'));
// { name: 'John', email: 'john@example.com' }

5. Partial Application

function partial(fn, ...presetArgs) {
  return function (...laterArgs) {
    return fn(...presetArgs, ...laterArgs);
  };
}

function greet(greeting, punctuation, name) {
  return `${greeting}, ${name}${punctuation}`;
}

const casualGreet = partial(greet, 'Hey', '!');
console.log(casualGreet('Alice')); // "Hey, Alice!"

const formalGreet = partial(greet, 'Good day', '.');
console.log(formalGreet('Mr. Smith')); // "Good day, Mr. Smith."

Best Practices

āœ… Use Rest for Flexible APIs

// Good: Flexible function signature
function createUser(name, ...options) {
  const [email, age, role = 'user'] = options;
  return { name, email, age, role };
}

// Can be called multiple ways
createUser('John');
createUser('John', 'john@example.com');
createUser('John', 'john@example.com', 30, 'admin');

āœ… Use Rest for Clean Extraction

// Good: Extract first item and keep rest
const [winner, ...runnerUps] = sortedParticipants;

// Good: Remove known properties
const { id, createdAt, ...updateData } = userInput;

āœ… Combine with Spread for Transformations

// Good: Transform while preserving other items
function updateFirst(fn, [first, ...rest]) {
  return [fn(first), ...rest];
}

const doubled = updateFirst((x) => x * 2, [5, 10, 15]);
console.log(doubled); // [10, 10, 15]

āŒ Avoid Overuse

// Unnecessary rest
function bad(...args) {
  const [a, b] = args; // Just use regular params
  return a + b;
}

// Better
function good(a, b) {
  return a + b;
}

Summary

ContextSyntaxPurpose
Function paramsfunction fn(...args)Collect all/remaining arguments
Function paramsfunction fn(a, ...rest)Collect remaining arguments
Array destructuring[a, ...rest] = arrCollect remaining elements
Object destructuring{a, ...rest} = objCollect remaining properties

Key Points

  • •Rest operator collects elements into an array/object
  • •Must be the last in parameter list or destructuring
  • •Creates a real array (unlike arguments)
  • •Works with arrow functions (unlike arguments)
  • •Empty rest results in empty array [] or empty object {}

Next Steps

After mastering the rest operator, proceed to:

  1. •3.9 Nullish Coalescing - Handle null and undefined elegantly
  2. •Practice combining rest and spread
  3. •Build utility functions using rest parameters
.8 Rest Operator - JavaScript Tutorial | DeepML