Rest Parameters
Collect any number of arguments into a real array with ...rest.
Basics
...rest collects remaining arguments into an array.
// Collect all arguments:
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
sum(); // 0
// 'numbers' is a real Array:
function show(...args) {
console.log(Array.isArray(args)); // true
console.log(args.length);
args.forEach(a => console.log(a));
args.map(a => a * 2); // all array methods work!
}
// Accepts any number of arguments:
function log(...messages) {
messages.forEach(msg => console.log(msg));
}
log("hello");
log("a", "b", "c", "d");vs arguments
Rest parameters replace the old arguments object.
// Old way — arguments object:
function old() {
console.log(arguments[0]); // works
arguments.forEach(x => x); // ❌ TypeError!
// arguments is array-LIKE, not a real array
// Had to convert:
var args = Array.prototype.slice.call(arguments);
args.forEach(x => console.log(x)); // now works
}
// New way — rest parameters:
function modern(...args) {
console.log(args[0]); // works
args.forEach(x => console.log(x)); // works!
args.map(x => x * 2); // works!
// It's already a real array
}Rest parameters give you a real Array with all methods. The old arguments object was array-like but not an actual Array.
1 / 2
With Other Params
Combine named parameters with rest — rest must be last.
// Named params + rest (rest must be LAST):
function tag(name, ...classes) {
return `<${name} class="${classes.join(" ")}">`;
}
tag("div", "container", "flex", "p-4");
// '<div class="container flex p-4">'
// First arg is special, rest collected:
function printf(template, ...values) {
return template.replace(/%s/g, () => values.shift());
}
printf("Hello %s, you have %s items", "Alice", 3);
// "Hello Alice, you have 3 items"
// ❌ Rest must be the last parameter:
function bad(...rest, last) {} // SyntaxError!
function bad(...a, ...b) {} // SyntaxError! (only one rest)
// ✓ These work:
function ok(a, b, ...rest) {}
function ok(...all) {}In Destructuring
Rest works in array and object destructuring too.
// Array destructuring — collect remaining:
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first = 1, second = 2, rest = [3, 4, 5]
const [head, ...tail] = "hello".split("");
// head = "h", tail = ["e", "l", "l", "o"]
// Object destructuring — collect remaining properties:
const { name, age, ...details } = {
name: "Alice", age: 25, city: "Tokyo", role: "dev"
};
// name = "Alice", age = 25
// details = { city: "Tokyo", role: "dev" }
// Remove properties immutably:
const { password, ...safeUser } = user;
// safeUser has everything except password
// Nested:
const { data: { id, ...metadata }, ...response } = apiResult;
// In function parameters:
function UserCard({ name, avatar, ...rest }) {
// rest = all other props
return <div {...rest}>{name}</div>;
}Patterns
Common real-world uses of rest parameters.
// 1. Wrapper/decorator functions:
function withLogging(fn) {
return function(...args) {
console.log(`Calling ${fn.name} with`, args);
const result = fn(...args); // spread back out!
console.log(`Result:`, result);
return result;
};
}
const loggedAdd = withLogging((a, b) => a + b);
loggedAdd(2, 3); // logs args, returns 5
// 2. Pipe/compose:
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
const process = pipe(
x => x * 2,
x => x + 1,
x => `Result: ${x}`
);
process(5); // "Result: 11"
// 3. Event emitter:
class EventBus {
#handlers = {};
on(event, fn) { (this.#handlers[event] ??= []).push(fn); }
emit(event, ...args) {
this.#handlers[event]?.forEach(fn => fn(...args));
}
}
// 4. Partial application:
function partial(fn, ...presetArgs) {
return (...laterArgs) => fn(...presetArgs, ...laterArgs);
}
const add10 = partial((a, b) => a + b, 10);
add10(5); // 15FAQ
Common questions about rest parameters.