Skip to content

Optional, Default, and Rest Parameters

Most functions have simple required parameters. But real functions often need flexibility: a parameter the caller may omit, a sensible default value, or an arbitrary number of arguments. TypeScript types all three patterns.

Mark a parameter optional with ?. Its type becomes T | undefined, and it must come after all required parameters:

function buildUrl(amount: number, difficulty?: Difficulty): string {
let url = `https://opentdb.com/api.php?amount=${amount}&type=multiple`;
if (difficulty) url += `&difficulty=${difficulty}`;
return url;
}
buildUrl(10); // ✓ — difficulty is undefined
buildUrl(10, 'easy'); // ✓ — difficulty is 'easy'
buildUrl(10, 'expert'); // Error: '"expert"' is not assignable to type 'Difficulty | undefined'

Inside the function body, TypeScript knows difficulty is Difficulty | undefined. The if (difficulty) guard narrows it to Difficulty before use.

A default parameter provides a fallback value when the argument is absent. The type is inferred from the default — no annotation needed:

function buildUrl(amount = 10, difficulty?: Difficulty): string {
// amount is number — inferred from the default value 10
let url = `https://opentdb.com/api.php?amount=${amount}&type=multiple`;
if (difficulty) url += `&difficulty=${difficulty}`;
return url;
}
buildUrl(); // ✓ — amount is 10
buildUrl(15); // ✓ — amount is 15
buildUrl(15, 'hard'); // ✓

Default parameters are optional at the call site — callers can omit them. Unlike ? optional parameters, default parameters are never undefined inside the function body. The default fills in before the function starts running.

When the inferred type is too wide, annotate the parameter explicitly alongside the default:

function fetchQuestions(amount: number = 10, difficulty?: Difficulty): Promise<TriviaQuestion[]> {
// amount is number, never undefined
// difficulty is Difficulty | undefined
}

Here amount gets an explicit : number annotation even though it has a default — making the type visible in the function signature.

Use ? (optional, no default) when the absence of the argument genuinely changes behavior:

// No difficulty → API returns questions of any difficulty
// With difficulty → API filters to that difficulty
async function fetchQuestions(amount: number, difficulty?: Difficulty)

Use a default when you want fallback behavior but the logic stays the same:

// Always fetches some number of questions — 10 if caller doesn't specify
async function fetchQuestions(amount: number = 10, difficulty?: Difficulty)

Both compile fine. The difference is semantics: a default communicates “this is the normal value”, while ? communicates “this genuinely might not be there”.

A rest parameter collects all remaining arguments into a typed array. It must be the last parameter and is annotated with ...name: T[]:

function logAll(...messages: string[]): void {
messages.forEach(m => console.log(m));
}
logAll('Question loaded', 'Timer started', 'UI updated'); // ✓
logAll(); // ✓ — zero arguments is valid

TypeScript enforces that every argument matches the element type:

logAll('Start', 42); // Error: Argument of type 'number' is not assignable to parameter of type 'string'

Rest parameters are less common in AceIt’s source, but they appear in the helpers you use daily — console.log, Promise.all, and array methods like push all use rest internally.

Parameters can combine all three patterns, in order: required → default → optional → rest:

function fetchWithOptions(
url: string,
amount = 10,
difficulty?: Difficulty,
...tags: string[]
): Promise<TriviaQuestion[]> {
// url is required, amount defaults to 10,
// difficulty is optional, tags collects remaining strings
}

In practice, signatures this complex are rare. If a function needs many parameters, an options object (a single parameter with a typed interface) is usually cleaner.

In api.ts:

  1. Add a default of 10 to the amount parameter of fetchQuestions. Confirm that fetchQuestions() (no arguments) and fetchQuestions(15, 'hard') both compile.
  2. Write a helper function buildUrl(amount: number, difficulty?: Difficulty, category?: number): string that appends each present parameter to the Open Trivia DB URL. Use optional parameters for difficulty and category.
  3. Write a function function joinMessages(...parts: string[]): string that joins all parts with ' | '. Call it with two, three, and zero arguments.
  • ? marks a parameter as optional — its type is T | undefined, and it must follow required parameters.
  • Default parameters provide a fallback value — they are never undefined inside the function body.
  • Use ? when absence changes behavior; use a default when the logic is the same but a fallback is useful.
  • Rest parameters (...name: T[]) collect remaining arguments into a typed array — must be last.
  • An options object interface is usually cleaner than many optional/rest parameters.