In simple language, sometimes we don’t want every parameter to be mandatory. TypeScript gives us three tools for that: optional (?), default values, and rest (...).
Optional parameters
Add a ? after the name and the parameter becomes optional. Inside the function, its type becomes T | undefined, so we still need to handle the “not passed” case.
function greet(name: string, title?: string): string {
// title is string | undefined here
return title ? `${title} ${name}` : name;
}
greet("Manish"); // OK
greet("Manish", "Dr."); // OK
Rule: optional parameters must come AFTER required ones. function bad(a?: string, b: number) is a compile error.
Default parameters
A default value makes the parameter optional AND gives it a fallback. Now we don’t need the | undefined check inside.
function greet(name: string, title: string = "Mr."): string {
return `${title} ${name}`; // title is just string, never undefined
}
greet("Manish"); // "Mr. Manish"
greet("Manish", "Dr."); // "Dr. Manish"
greet("Manish", undefined); // "Mr. Manish" — undefined triggers default
Defaults can be after required params too, and they can even reference earlier parameters. They don’t need to be at the end IF the call site passes undefined explicitly.
Rest parameters
When we don’t know how many arguments will come in, we collect them into an array with .... Think of it like the opposite of spread.
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
sum(); // 0
sum(...[1, 2, 3]); // 6 — spread back in
Rest must be the last parameter, and its type is always an array (or tuple).
Tuples in rest — the powerful trick
We can type rest params as a tuple to enforce specific positions. Super useful for things like event emitters.
type EventArgs = [event: string, payload: object];
function emit(...args: EventArgs) {
const [event, payload] = args;
console.log(event, payload);
}
emit("user.created", { id: 1 }); // OK
Common interview gotcha
What’s the difference between param?: string and param: string | undefined? The optional version lets us SKIP the argument at the call site; the union version forces us to pass undefined explicitly. Subtle but it shows up in API design.
function a(x?: string) {}
function b(x: string | undefined) {}
a(); // OK
b(); // Error — expected 1 argument
b(undefined); // OK