Skip to Content
TypeScript for Beginners: Catch Wrong Data Before It Runs
Beginner
0 likes
Courses
TypeScript for Beginners: Catch Wrong Data Before It Runs

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.

Course Home

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.12 and breaking on 0, "" or false.
  • Putting a required parameter after an optional one.
  • Making a parameter optional when a default would remove the check.
  • Passing undefined in 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
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.