Optional and Default Parameters Without Surprises
Choose between an optional parameter and a default, avoid the zero bug of the or operator, and pass an options object when there are too many. After this, calls with missing arguments behave as you expect.
Course content
Sign in to save your progress.
-
Before You Write Types
-
What TypeScript Adds on Top of JavaScript
-
Install TypeScript and Run Your First .ts File
-
-
-
Types You Cannot Skip
-
Annotating Variables Without Fighting the Compiler
-
Arrays, Objects and Nested Shapes
-
Optional, Readonly and Why any Is a Trap
-
-
-
Functions That State Their Deal
-
Parameter and Return Types
-
Optional and Default Parameters Without Surprises
-
-
-
Shapes You Can Reuse
-
Interfaces vs type Aliases: Pick One and Stick With It
-
Unions, Literals and Narrowing With typeof and in
-
-
-
Compile Without the Mystery
-
tsconfig.json: target, module and strict
-
Reading tsc Errors Without Panicking
-
-
- Final Check: TypeScript for Beginners
Skip ahead: If you already know that a default value only applies when the argument is undefined, and that optional parameters must come last, skip to "A bug that defaults avoid", then continue.
Some arguments are not needed every time. A tax rate, a currency, a page size. JavaScript lets you leave them off and does nothing to tell you what happens next. TypeScript gives you two ways to say "this one may be missing", and they behave differently enough to cause real bugs.
Optional parameters
Add ? after the name.
function priceLabel(price: number, currency?: string): string {
return price + " " + currency.toUpperCase();
}
function badOrder(currency?: string, price: number): string {
return price + " " + currency;
}
optional-param.ts(2,24): error TS18048: 'currency' is possibly 'undefined'.
optional-param.ts(5,38): error TS1016: A required parameter cannot follow an optional parameter.
Inside the function, currency has the type string | undefined, so using it as a string without a check is an error. The compiler is correct: if the caller leaves it off, currency.toUpperCase() would crash. You handle the missing case, as you did with optional properties.
The second error is a rule you can remember as "the optional ones go last". A caller cannot skip a parameter in the middle, so an optional one followed by a required one makes no sense.
Default parameters
A default value says what to use when the caller passes nothing.
function priceLabel(price: number, currency = "PHP"): string {
return price + " " + currency.toUpperCase();
}
console.log(priceLabel(250));
console.log(priceLabel(250, "usd"));
250 PHP
250 USD
There is no annotation on currency, and none is needed. The compiler infers string from the default. Inside the function it is a plain string, never undefined, so there is nothing to check. That is why a default is the better choice whenever a sensible default exists.
Callers can still leave it off. A parameter with a default is optional from the outside, without the ?.
A bug that defaults avoid
Before default parameters, people wrote rate = rate || 0.12. That treats 0 as missing. Here is the same function written both ways.
function taxWithOr(amount: number, rate?: number): number {
const r = rate || 0.12;
return amount * r;
}
function taxWithDefault(amount: number, rate = 0.12): number {
return amount * rate;
}
console.log(taxWithOr(100, 0));
console.log(taxWithDefault(100, 0));
console.log(taxWithDefault(100, undefined));
12
0
12
A rate of 0 is a real value: a tax-free item. The || version throws it away and charges 12. The default version keeps it. A default only applies when the argument is undefined, which is also why passing undefined explicitly gives you the default, as in the last line.
null is not undefined. Passing null to a parameter that is typed number is an error under strict mode, so it will not reach the default by accident.
⚠ Warning: If you take a value from user input or JSON and it can be null, the default will not rescue it. Handle null before the call, or use ?? inside.
Too many optionals
Three positional optional parameters make calls like format(100, undefined, undefined, true). Nobody can read that. Past two, pass an object instead.
type FormatOptions = {
currency?: string;
decimals?: number;
};
function format(price: number, options: FormatOptions = {}): string {
const currency = options.currency ?? "PHP";
const decimals = options.decimals ?? 2;
return price.toFixed(decimals) + " " + currency;
}
console.log(format(99.5));
console.log(format(99.5, { decimals: 0 }));
99.50 PHP
100 PHP
The names are now at the call site, the order stops mattering, and adding a new option does not break existing calls. Notice the = {} default on the whole object, so format(99.5) still works.
Common mistakes
- Writing
rate || 0.12and breaking on0,""orfalse. - Putting a required parameter after an optional one.
- Making a parameter optional when a default would remove the check.
- Passing
undefinedin the middle of a long argument list to reach a later parameter.
💡 Tip: Choose a default when the missing case has a sensible value. Choose ? when "not given" means something different from any value.
If you skip this, you will write || fallbacks that eat zeros, and the bug will show up as a wrong total only for the one customer whose rate is zero. Which of your own functions has an argument where 0 is a legitimate answer?
About
Choose between an optional parameter and a default, avoid the zero bug of the or operator, and pass an options object when there are too many. After this, calls with missing arguments behave as you expect.
- Duration
- 5 min
- Last update
- 10/05/2026
Comments (0)
Sign in to commentNo comments yet.