Course outline · 0% complete

0/26 lessons0%

Course overview →

Optional and Default Parameters

lesson 5-1 · ~11 min · 15/26

Lesson 3-2 used bio?: string to make an object property optional, and the same symbol appears on parameters in function greet(name: string, title?: string).

The meaning carries over exactly: ? marks something that may be absent. A caller can write greet("Ada") or greet("Ada", "Dr."), and inside the function you must handle the undefined case before using title as a string.

That symmetry is deliberate. Optionality is one idea in TypeScript, whether it applies to a property in a shape or to an argument in a call.

Parameters callers may skip

In JavaScript every parameter was silently optional, missing ones just became undefined. TypeScript flips that: parameters are required unless you say otherwise.

function greet(name: string, title?: string): string

title? allows the two-argument and one-argument calls, and inside the body title has type string | undefined, the union from Unit 4. Narrow it with a check before use.

A default parameter goes one step further and supplies the fallback value itself:

function ticketPrice(age: number, discount: number = 0): number

With a default, the parameter is optional for callers but never undefined inside the body, so no check is needed. Optional parameters must come after required ones.

Calling a function with and without its optional argument

function greet(name: string, title?: string): string {
  if (title !== undefined) {
    return "Hello, " + title + " " + name;
  }
  return "Hello, " + name;
}

console.log(greet("Ada"));
console.log(greet("Ada", "Dr."));

Output

Hello, Ada
Hello, Dr. Ada

Both calls compile because only the second parameter is optional. Calling greet() with no arguments at all is an error, since name is marked neither with ? nor with a default value, and only parameters that carry one of those may be omitted.

The if (title !== undefined) guard is required rather than stylistic. Inside the function title has type string | undefined, so the guard is what narrows it to a plain string before it is concatenated.

ticketPrice: a parameter with a default value

ticketPrice(age: number, discount: number = 0) charges 5 for children under 12 and 12 for everyone else, then subtracts any discount.

function ticketPrice(age: number, discount: number = 0): number {
  const base = age < 12 ? 5 : 12;
  return base - discount;
}

console.log(ticketPrice(30));
console.log(ticketPrice(8, 2));

Output

12
3

How the default changes things

  • A default value is written straight into the signature as discount: number = 0. There is no separate declaration and no check at the top of the body.
  • Because of the default, ticketPrice(30) is a legal call and discount is 0 inside the function, not undefined. That is the key difference from an optional parameter: the type inside the body stays plain number, so no narrowing is needed.
  • age < 12 ? 5 : 12 is the conditional operator from lesson 3-2 choosing the base price, and ticketPrice(8, 2) therefore computes 5 - 2.

Rest parameters: any number of arguments

console.log happily takes one argument or five, and your own functions can too. A rest parameter collects every remaining argument into an array. Declare it with ... and give it an array type:

function sum(...nums: number[]): number

Callers write sum(1, 2, 3) or sum(). Inside the body, nums is an ordinary number[] you can loop over.

Two rules follow from what it is. A rest parameter must come last, because it absorbs everything after it, and its type must be an array type, because an array is what the collected arguments become.

The payoff is that variable-argument functions stay fully checked. sum(1, "2") is a compile error, not a NaN discovered at runtime.

sum: adding a variable number of arguments

sum(...nums: number[]) adds every argument it receives and returns the total, whether that is three arguments, two, or none at all.

function sum(...nums: number[]): number {
  let total = 0;
  for (const n of nums) {
    total += n;
  }
  return total;
}

console.log(sum(1, 2, 3));
console.log(sum(10, 20));
console.log(sum());

Output

6
30
0

Reading the rest parameter

  • The three dots go in the declaration, as function sum(...nums: number[]). Callers never write dots, they just pass arguments normally.
  • Inside the body nums is an ordinary number[], so the for...of loop is the same one you have written since the JavaScript course.
  • With no arguments at all, nums is an empty array, the loop body never runs, and the initial total of 0 comes back. That is the natural identity for addition, so the empty case needs no special handling.
  • Because the element type is number, a call like sum(1, "2") is a compile error rather than a NaN discovered later at runtime.