Rest Parameters in TypeScript

A rest parameter collects any number of remaining arguments into a typed array. Learn the ... syntax, how to type rest parameters, and the difference between rest parameters and spread arguments.

5 min read

TypeScript rest parameters collect any number of arguments into a single typed array. You write three dots before the parameter name, and TypeScript types it as an array of the element type. It must be the last parameter in the function signature.

typescripttypescript
function sum(...numbers: number[]): number {
  return numbers.reduce((total, n) => total + n, 0);
}
 
console.log(sum(1, 2, 3));
console.log(sum(10, 20, 30, 40, 50));
// Output: 6
// Output: 150

The caller can pass any number of arguments. Inside the function, the parameter is a plain array containing all the arguments. When no arguments are passed, the array is empty rather than undefined:

typescripttypescript
console.log(sum());
// Output: 0

The Rest Parameter Syntax

A rest parameter follows three rules: it starts with ..., it has an array type, and it appears last in the parameter list. Any named parameters before it still work exactly like normal parameters:

typescripttypescript
function logAll(prefix: string, ...messages: string[]): void {
  for (const msg of messages) {
    console.log(`${prefix}: ${msg}`);
  }
}
 
logAll("INFO", "Server started", "Listening on port 3000");
// Output: INFO: Server started
// Output: INFO: Listening on port 3000

The first parameter receives the first argument. Everything after that goes into the rest parameter as an array of strings. The rest parameter always collects the remaining positional arguments after all named parameters are satisfied, no matter how many arguments the caller passes.

Typing Rest Parameters

The type annotation on a rest parameter must be an array type. You can write T[] or the generic form Array<T>:

typescripttypescript
// Array shorthand
function join(separator: string, ...parts: string[]): string {
  return parts.join(separator);
}
 
// Array generic
function multiply(factor: number, ...values: Array<number>): number[] {
  return values.map((v) => v * factor);
}

Both functions accept any number of arguments, and calling each one shows the shorthand and generic forms behave identically at the call site:

typescripttypescript
console.log(join(" - ", "2026", "07", "17"));
console.log(multiply(2, 1, 2, 3));
// Output: 2026 - 07 - 17
// Output: [2, 4, 6]

Both the shorthand and generic forms are valid. The shorthand T[] is more common and preferred in most codebases.

You can also use a union as the element type when the rest parameter accepts mixed arguments:

typescripttypescript
function logValues(...values: (string | number)[]): void {
  values.forEach((v) => {
    if (typeof v === "string") {
      console.log(`String: ${v.toUpperCase()}`);
    } else {
      console.log(`Number: ${v.toFixed(2)}`);
    }
  });
}

Calling it with a mix of strings and numbers shows why narrowing is required inside the loop, since each call needs a different formatting method:

typescripttypescript
logValues("hello", 42, "world", 7);
// Output: String: HELLO
// Output: Number: 42.00
// Output: String: WORLD
// Output: Number: 7.00

Inside the function, each element has the type string | number, so you must narrow before using type-specific methods.

Rest Parameters vs Separate Parameters

Use a rest parameter when callers need to pass a variable number of values of the same type. Use separate parameters when each argument has a different meaning:

SituationChoiceExample
Each argument means something differentSeparate parametersname, age, email
Arguments are a collection of the same kind of valueRest parametera list of tags or ids
Number of arguments is unknown ahead of timeRest parametera logger with variable messages
typescripttypescript
// Separate parameters: each has a distinct role
function createUser(name: string, age: number, email: string) {
  return { name, age, email };
}
 
// Rest parameter: all arguments play the same role
function createList(...items: string[]) {
  return items;
}

Calling each function shows the difference in practice: one builds a structured object from distinct fields, and the other collects same-shaped values into a list:

typescripttypescript
console.log(createUser("Alice", 30, "alice@example.com"));
console.log(createList("read", "write", "execute"));
// Output: { name: "Alice", age: 30, email: "alice@example.com" }
// Output: ["read", "write", "execute"]

The first function's parameters each represent a different field. The second function's parameters are all the same kind of value. A rest parameter signals that the arguments are a collection, not individual properties.

Spread Arguments at the Call Site

The ... syntax also works in reverse at the call site. You can spread an array into individual arguments:

typescripttypescript
function multiply(a: number, b: number, c: number): number {
  return a * b * c;
}
 
const factors = [2, 3, 4] as const;
console.log(multiply(...factors));
// Output: 24

The as const assertion on the array is important. Without it, TypeScript infers the array as a plain number array, which could have any length. Removing the assertion and trying the same call demonstrates the problem directly:

typescripttypescript
const factors = [2, 3, 4];
console.log(multiply(...factors));

The compiler rejects this call because a plain array type might have any number of elements at all, even though this particular array happens to have exactly three:

texttext
A spread argument must either have a tuple type or be passed to a rest parameter.

The as const assertion tells TypeScript the array has exactly three elements with literal types. For more on this assertion, see Use as const in TypeScript.

When the target function uses a rest parameter, the spread works without that assertion:

typescripttypescript
function logAll(...messages: string[]): void {
  messages.forEach((m) => console.log(m));
}
 
const msgs = ["one", "two", "three"];
logAll(...msgs);
// Output: one
// Output: two
// Output: three

Since this function accepts a rest parameter, TypeScript allows spreading any string array into it. The array length does not matter because the function accepts any number of arguments.

Rest Parameters With Tuples

For cases where you need different types at different positions, use a tuple type for the rest parameter:

typescripttypescript
function parseArgs(...args: [string, number]): void {
  const [name, count] = args;
  console.log(`${name}: ${count}`);
}
 
parseArgs("items", 5);
// Output: items: 5

The tuple [string, number] means the function expects exactly a string followed by a number. The rest parameter collects them into a typed tuple. This is less common than using separate parameters but useful when the function receives the arguments as an array to begin with.

When the caller needs to pass any number of same-shaped pairs rather than exactly one, type the rest parameter as an array of tuples instead of a single tuple:

typescripttypescript
function pairUp(...pairs: [string, number][]): void {
  pairs.forEach(([name, value]) => {
    console.log(`${name} = ${value}`);
  });
}
 
pairUp(["x", 1], ["y", 2], ["z", 3]);
// Output: x = 1
// Output: y = 2
// Output: z = 3

This type means the function accepts any number of string-number pairs. Each argument is a tuple, and the rest parameter collects them all into one array of tuples. This is a different feature from a variadic tuple type, which uses a spread inside a single tuple to describe one value with a fixed first element and a variable-length tail.

Common Mistakes

Putting a parameter after the rest parameter. A rest parameter must be the last one. TypeScript reports an error if you try to add another parameter after it:

typescripttypescript
// Error: A rest parameter must be last in a parameter list
function bad(...items: string[], count: number) {
  console.log(items, count);
}

Forgetting that a rest parameter is always an array. Inside the function, the rest parameter is an array, even when only one argument is passed:

typescripttypescript
function logFirst(...items: string[]) {
  console.log(items[0]);
}
 
logFirst("only one");
// Output: only one

The rest parameter holds a one-element array, not a plain string. Always use array methods or indexing to access rest parameter values.

Using as const on an array spread when the target uses a rest parameter. That assertion is only needed when the target function has fixed parameters. For rest parameter targets, a regular array works without it.

For more on function parameters, see type arrow functions in TypeScript and type callback functions in TypeScript.

Rune AI

Rune AI

Key Insights

  • A rest parameter uses ... before the name: ...items: string[].
  • The rest parameter collects all remaining arguments into a typed array.
  • It must be the last parameter, and there can only be one.
  • A rest parameter is always an array inside the function, even with zero arguments.
  • Use spread syntax (...myArray) at the call site to unpack an array into arguments.
RunePowered by Rune AI

Frequently Asked Questions

What type does a rest parameter have?

A rest parameter is typed as an array. For example, ...items: string[] gives items the type string[]. You can also use tuple types for more precise control over individual argument positions.

Can a function have more than one rest parameter?

No. A rest parameter must be the last parameter, and there can only be one. It collects all remaining arguments, so a second rest parameter would have nothing left to collect.

How do I spread an array as arguments to a function in TypeScript?

Use the same ... syntax at the call site: myFunc(...myArray). TypeScript checks that the spread array is compatible with the function's parameters. Use as const on the array to get tuple inference when the function expects a fixed number of arguments.

Conclusion

A rest parameter collects a variable number of arguments into a typed array. Use ... before the last parameter name, and type it as an array like string[] or number[]. The rest parameter is always an array inside the function, even when the caller passes zero arguments for it. At the call site, use spread syntax to unpack an array into individual arguments.