Optional, default and rest parameters
Not every call passes every argument. Some are optional, some have sensible defaults, and some functions take any number of arguments. TypeScript types all three — optional, default, and rest parameters — and knowing how each affects the types (and which to prefer) is everyday knowledge.
Optional parameters — ?
A ? after a parameter name makes it optional; callers may omit it:
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
greet("Kavita"); // fine — title omitted
greet("Kavita", "Dr."); // fine
Inside the function, an optional parameter has type T | undefined — it might be the value or absent —
so you must handle the undefined case (here, the title ? check). This is the same as an optional
property from the last module: title?: string is really string | undefined, and TypeScript
forces you to account for the missing case.
Two rules for optional parameters:
- They must come after required ones.
function f(a?: string, b: string)is an error — an optional parameter cannot precede a required one, because then omitting it would be ambiguous. - Prefer a default (below) when there is a sensible fallback. An optional parameter leaves you with
undefinedto handle; a default gives you a real value.
Default parameters — a value when omitted
A default provides a value used when the caller omits the argument:
function greet(name: string, greeting: string = "Namaste"): string {
return `${greeting}, ${name}`;
}
greet("Kavita"); // "Namaste, Kavita"
greet("Kavita", "Good morning"); // "Good morning, Kavita"
greeting: string = "Namaste" means "if the caller does not pass greeting, use "Namaste"". Two nice
things about defaults in TypeScript:
- The type is inferred from the default. You can write
greeting = "Namaste"without: string— TypeScript infersstringfrom the default value. (Annotate it if the default does not fully determine the type you want.) - Inside the function, the parameter is not optional. Because a default is always applied,
greetinghas typestring(neverundefined) inside the body — noundefinedto handle. This is the advantage over an optional parameter: a default gives you a guaranteed value.
So the guidance: when there is a sensible fallback, use a default, not an optional parameter. You
avoid the undefined check entirely. Use an optional parameter only when "absent" is genuinely
different from any default value (when the function should behave differently when the argument is
truly missing).
Rest parameters — any number of arguments
A rest parameter collects any number of trailing arguments into an array:
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
sum(); // 0
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
...numbers: number[] means "gather all remaining arguments into a number[]". Inside the function,
numbers is an ordinary array. TypeScript checks that every argument passed matches the element type:
sum(1, 2, "three"); // error: Argument of type 'string' is not assignable to parameter of type 'number'
A rest parameter must be last, and there can be only one. It can follow fixed parameters:
function log(level: string, ...messages: string[]): void {
console.log(`[${level}]`, ...messages);
}
log("INFO", "started", "on", "port", "3000"); // level = "INFO", messages = the rest
Rest parameters are how you type functions like console.log or a max(...nums) that take a variable
number of arguments — a genuinely common need that other languages handle more awkwardly.
Spreading into a call
The mirror image of a rest parameter is the spread at a call site — passing an array as individual
arguments with ...:
const nums = [1, 2, 3];
sum(...nums); // spreads the array into sum(1, 2, 3) — 6
function point(x: number, y: number) { /* ... */ }
const coord: [number, number] = [18.52, 73.85];
point(...coord); // spreads the tuple into point(18.52, 73.85) — checked!
Spreading a tuple into a call is especially nice — TypeScript checks the tuple's length and element
types against the parameters, so point(...coord) is fully type-safe. This is the connection between
tuples (from the arrays lesson) and function calls, and it is how typed code passes a fixed group of
values cleanly.
Choosing among them
- Required parameter — the argument is always needed.
- Default parameter — there is a sensible fallback; the caller can omit it and get a real value. Prefer this over optional when a default makes sense.
- Optional parameter (
?) — the argument may be genuinely absent, and "absent" means something different from any default; you handle theundefined. - Rest parameter (
...) — any number of trailing arguments of one type, collected into an array; must be last.
The most common beginner improvement is replacing an optional parameter plus a ?? default inside the
body with a default parameter — it is shorter and removes the undefined from the type. Reach for a
default first.
Check your work
What ? on a parameter does. Makes it optional; inside the function it is T | undefined, which
you must handle.
Two rules for optional parameters. They must come after required ones; prefer a default when a sensible fallback exists.
What a default parameter does, and its type advantage. Provides a value when omitted, and inside
the function the parameter is not undefined — a guaranteed value, no check needed.
Optional versus default — the guidance. Use a default when there is a sensible fallback; an optional parameter only when "absent" is genuinely distinct.
What a rest parameter does. Collects any number of trailing arguments into an array; must be last, and there is only one.
What TypeScript checks on a rest parameter. That every passed argument matches the element type.
What spreading a tuple into a call gives you. Type-checked argument passing — the tuple's length and element types are checked against the parameters.
Practice
- Write
greet(name, title?)and call it with and without the title. Handle theundefinedinside. - Rewrite it with a default
title = "Dr."and confirm you no longer handleundefinedinside. Decide which is more appropriate. - Try to put an optional parameter before a required one and read the error.
- Write
sum(...numbers: number[]). Call it with zero, three, and five arguments. Then pass a string argument and read the error. - Write a function with a fixed parameter followed by a rest parameter (
log(level, ...messages)). - Spread an array into a variadic call (
sum(...nums)) and confirm it works. - Spread a
[number, number]tuple into a two-parameter function and confirm the length and types are checked.
Official documentation
- TypeScript — Optional and default parameters —
?and defaults. - TypeScript — Rest parameters and spread —
...in both positions. - TypeScript — Function parameter destructuring — A related everyday pattern.
Next: function types and callbacks.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship