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.
Optional parameters
Section titled “Optional parameters”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 undefinedbuildUrl(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.
Default parameters
Section titled “Default parameters”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 10buildUrl(15); // ✓ — amount is 15buildUrl(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.
Annotating default parameters explicitly
Section titled “Annotating default parameters explicitly”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.
Optional vs default — when to use each
Section titled “Optional vs default — when to use each”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 difficultyasync 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 specifyasync 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”.
Rest parameters
Section titled “Rest parameters”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 validTypeScript 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.
Combining all three
Section titled “Combining all three”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.
Exercise
Section titled “Exercise”In api.ts:
- Add a default of
10to theamountparameter offetchQuestions. Confirm thatfetchQuestions()(no arguments) andfetchQuestions(15, 'hard')both compile. - Write a helper
function buildUrl(amount: number, difficulty?: Difficulty, category?: number): stringthat appends each present parameter to the Open Trivia DB URL. Use optional parameters fordifficultyandcategory. - Write a function
function joinMessages(...parts: string[]): stringthat joins all parts with' | '. Call it with two, three, and zero arguments.
?marks a parameter as optional — its type isT | undefined, and it must follow required parameters.- Default parameters provide a fallback value — they are never
undefinedinside 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.