TypeScript Optional and Default Parameters (with Examples)

Optional parameters may be left out and are undefined inside the function; default parameters fill in a value instead. Examples cover undefined vs null, order rules and options objects.

In TypeScript, every function parameter is required unless we mark it optional with a question mark (middle?: string) or give it a default value (greeting = “Hello”). An optional parameter may be left out by the caller and is undefined inside the function when missing. A default parameter may also be left out, but the function receives the default value instead of undefined.

We use both to let one function serve callers that have more or less information, without writing several versions of the function. For example, a sign-up form asks for a middle name, but most users leave it empty, so the name formatter takes the middle name as an optional parameter.

The following example shows an optional parameter and a default value, plus an options object whose properties have defaults. It uses TypeScript 7.0 in strict mode and Node.js 22.

// 1. Optional parameter with ?
function fullName(first: string, last: string, middle?: string): string {
  return middle ? first + " " + middle + " " + last : first + " " + last;
}
const n1 = fullName("Lokesh", "Gupta");       // n1 = "Lokesh Gupta"
const n2 = fullName("Raj", "Kumar", "Pal");   // n2 = "Raj Pal Kumar"

// 2. Default value
function greet(name: string, greeting = "Hello"): string {
  return greeting + ", " + name;
}
const g1 = greet("John");                     // g1 = "Hello, John"
const g2 = greet("John", "Hi");               // g2 = "Hi, John"
const g3 = greet("John", undefined);          // g3 = "Hello, John" (undefined uses the default)

// 3. Options object with defaults
interface PrintOptions {
  prefix?: string;
  upper?: boolean;
}
function print(name: string, { prefix = "-", upper = false }: PrintOptions = {}): string {
  const text = prefix + " " + name;
  return upper ? text.toUpperCase() : text;
}
const p1 = print("apple");                    // p1 = "- apple"
const p2 = print("apple", { upper: true });   // p2 = "- APPLE"

Notice that greet(“John”, undefined) gets the default value, the same as a call without the second argument.

Next, we look at how the parameter type changes, what undefined and null do, and the order rules with their compiler errors. The two features look simple, yet they cause real bugs, which the later sections fix.

  • A check that treats 0 or an empty string as “missing” skips valid values.
  • A default value does not apply to null.
  • A fourth optional parameter makes calls unreadable.

We finish with optional parameters in function types and callbacks. Rest parameters, which take any number of arguments, follow other rules and are not part of this guide.

1. Marking a Parameter Optional With ?

JavaScript lets us call any function with fewer arguments than it declares, and the missing ones are undefined. TypeScript is stricter, so a call with too few arguments is a compile error. When we put a question mark after the parameter name, we tell the compiler that the caller may leave the argument out. TypeScript calls such parameters optional parameters.

// 1. middle is optional
function fullName(first: string, last: string, middle?: string): string {
  return middle ? first + " " + middle + " " + last : first + " " + last;
}
const n1 = fullName("Lokesh", "Gupta");       // n1 = "Lokesh Gupta"
const n2 = fullName("Raj", "Kumar", "Pal");   // n2 = "Raj Pal Kumar"

// 2. Inside the function, the type includes undefined
function describeAge(age?: number): string {
  if (age === undefined) {
    return "unknown";
  }
  return age + " years";                      // age is number here
}
const a1 = describeAge();                     // a1 = "unknown"
const a2 = describeAge(37);                   // a2 = "37 years"
const a3 = describeAge(0);                    // a3 = "0 years"

For the caller, middle?: string is optional. Inside the function, the type of middle is string | undefined, and with strict mode the compiler stops us from using it as a plain string. Writing return age + 1 without the check fails with TS18048: ‘age’ is possibly ‘undefined’.

The function fullName() uses a truthy check (middle ? …), which is fine for a name, because an empty string should also be skipped. For numbers, a truthy check is a bug because 0 is falsy, so if describeAge() used if (!age), the call describeAge(0) would return “unknown”. We compare with undefined explicitly whenever 0, an empty string or false is a valid value. The number 0 and the empty string are both falsy values in JavaScript.

2. Giving a Parameter a Default Value

A default parameter has an initializer, such as greeting = “Hello”. If the caller leaves the argument out, the function uses the default, so the body never sees undefined and needs no check. TypeScript infers the parameter type from the default value, so greeting is a string without an annotation, and greet(“John”, 5) is a compile error.

// 1. Type is inferred from the default value
function greet(name: string, greeting = "Hello"): string {
  return greeting + ", " + name;
}
const g1 = greet("John");                     // g1 = "Hello, John"
const g2 = greet("John", "Hi");               // g2 = "Hi, John"

// 2. A default can use earlier parameters
function box(width: number, height = width): number {
  return width * height;
}
const square = box(5);                        // square = 25
const rect = box(5, 3);                       // rect = 15

// 3. The default is evaluated on every call
function addFruit(fruit: string, basket: string[] = []): string[] {
  basket.push(fruit);
  return basket;
}
const b1 = addFruit("apple");                 // b1 = ["apple"]
const b2 = addFruit("banana");                // b2 = ["banana"], a new array

A default value can be any expression, including a function call or a reference to a parameter declared before it, as height = width shows. The expression runs each time the function is called without that argument. The new evaluation on every call is why b2 gets its own empty array; developers coming from Python, where a default list is created once and shared between calls, often expect the opposite.

We choose a default over ? when a sensible value exists, because a default keeps the body simpler. If “no value” has its own meaning, such as “no middle name”, an optional parameter fits better.

3. Omitted Argument, undefined and null

A default value applies in two cases only.

  • The argument is left out.
  • The caller passes undefined.

A default value does not apply to null, because null is a real value. The null case trips up code that reads values from JSON or a database, where a missing field often arrives as null.

function city(name: string, home: string | null = "Delhi"): string {
  return name + " lives in " + home;
}

const c1 = city("Lokesh");                    // c1 = "Lokesh lives in Delhi"
const c2 = city("Raj", undefined);            // c2 = "Raj lives in Delhi"
const c3 = city("John", null);                // c3 = "John lives in null"

// Default for both null and undefined
function city2(name: string, home?: string | null): string {
  return name + " lives in " + (home ?? "Delhi");
}
const c4 = city2("John", null);               // c4 = "John lives in Delhi"

In this example, the type allows null on purpose to show the behavior. With a plain default like greeting = “Hello”, strict mode rejects null at compile time, so greet(“John”, null) fails with TS2345: Argument of type ‘null’ is not assignable to parameter of type ‘string | undefined’. The compile error protects us only when the value’s type is known; data parsed from JSON is often typed loosely, so the null reaches the function at runtime.

When both null and undefined should mean “use the default”, we make the parameter optional and apply the default with the nullish coalescing operator ??, as city2() does. The ?? operator returns its right side only for null and undefined, so 0 and empty strings pass through unchanged.

CallOptional home?: stringDefault home = “Delhi”
city(“Lokesh”)home is undefinedhome is “Delhi”
city(“Lokesh”, undefined)home is undefinedhome is “Delhi”
city(“Lokesh”, null)Compile error unless the type allows null; then nullCompile error unless the type allows null; then null
city(“Lokesh”, “”)home is “”home is “”

4. Rules for the Order of Parameters

The compiler enforces where optional and default parameters may appear.

  • An optional parameter cannot be followed by a required one, because the caller could not leave out the middle argument and still pass the last one.
  • A parameter cannot have both ? and a default value, since a default already makes it optional.
function fullName(first: string, middle?: string, last: string)
  error TS1016: A required parameter cannot follow an optional parameter.

function greet(name: string, greeting?: string = "Hello")
  error TS1015: Parameter cannot have question mark and initializer.

Default parameters are less strict, so a parameter with a default may come before a required one. In that position, the default applies only when the caller passes undefined, because the argument cannot be left out.

// 1. A default before a required parameter
function rate(fruit = "apple", count: number): string {
  return fruit + ": " + count;
}
const r1 = rate(undefined, 5);                // r1 = "apple: 5"
const r2 = rate("banana", 3);                 // r2 = "banana: 3"

// 2. Optional vs required-but-undefined
function optional(age?: number): string {
  return "age " + age;
}
function explicit(age: number | undefined): string {
  return "age " + age;
}
const o1 = optional();                        // o1 = "age undefined"
const o2 = explicit(undefined);               // o2 = "age undefined"; explicit() alone fails

The call rate(5) does not compile (TS2554: Expected 2 arguments, but got 1), so the default in front is rarely useful. We keep defaults at the end of the list.

Step 2 shows a related difference. The parameter age?: number lets the caller leave the argument out, whereas age: number | undefined accepts undefined as a value but still requires the caller to pass it, so explicit() gives TS2554. We use the second form when we want every caller to decide on the value consciously.

5. An Options Object With Destructured Defaults

Positional optional parameters stop working well after two or three, because a call like print(“apple”, undefined, true, 2) is hard to read, and the caller has to pass undefined to skip a value. For example, a report export function starts with a file name and grows options for the format, the date range and compression over the years. The usual fix is one options object, which is an interface with optional properties combined with destructuring and a default value per property.

interface PrintOptions {
  prefix?: string;
  upper?: boolean;
  times?: number;
}

function print(name: string, { prefix = "-", upper = false, times = 1 }: PrintOptions = {}): string {
  const text = (prefix + " " + name).repeat(times);
  return upper ? text.toUpperCase() : text;
}

const p1 = print("apple");                    // p1 = "- apple"
const p2 = print("apple", { upper: true });   // p2 = "- APPLE"
const p3 = print("kiwi", { times: 2, prefix: "*" }); // p3 = "* kiwi* kiwi"

Two defaults work together here.

  • Each property has its own default inside the braces.
  • The = {} at the end is the default for the whole object, so print(“apple”) works without a second argument.

Without the = {} default, the second argument becomes required, and a JavaScript caller that leaves it out gets a TypeError because there is nothing to destructure. Callers name only the settings they change, in any order, and adding a new option later does not break existing calls.

The trade-off is a little more code in the declaration, and the defaults are not visible in the PrintOptions interface, so for one or two optional values we keep plain parameters, which are shorter.

6. Optional Parameters in Function Types and Callbacks

A function type can mark a parameter optional with ?, but it cannot contain a default value, because a type has no code to run. Writing (name: string, greeting: string = “Hello”) => string gives TS2371: A parameter initializer is only allowed in a function or constructor implementation. The default belongs in the function that implements the type.

// 1. Optional parameter in a function type
type Greeter = (name: string, greeting?: string) => string;

// 2. A default value in the implementation satisfies it
const greet: Greeter = (name, greeting = "Hello") => greeting + ", " + name;
const g1 = greet("Lokesh");                   // g1 = "Hello, Lokesh"
const g2 = greet("Lokesh", "Hi");             // g2 = "Hi, Lokesh"

// 3. A callback may ignore parameters it does not need
const ages = [37, 35, 40];
const doubled = ages.map((age) => age * 2);   // doubled = [74, 70, 80]
const indexed = ages.map((age, i) => i + ":" + age); // indexed = ["0:37", "1:35", "2:40"]

The implementation must accept undefined wherever the type says the parameter is optional.

An implementation with a required greeting: string fails with TS2322, because callers of Greeter may leave the argument out.

Step 3 shows the rule for callbacks. The method map() always passes three arguments (value, index, array), and a callback that declares only age is still valid, because a function that takes fewer parameters can always be used where more are passed. So we do not mark callback parameters optional because some callbacks ignore them. A callback type should declare ? only when the caller may call it without that argument.

When the number of arguments changes the return type, or some combinations must be forbidden, optional parameters are not enough, and we use function overloading instead.

7. Running the Parameter Examples

The source code on GitHub splits the examples by section. A machine with Node.js 22 or newer is enough; the compiler version, TypeScript 7.0.2, is pinned in package.json.

npm install
npm start

After npm start, the terminal lists the results of fullName(), greet(), city() and the other functions in article order.

8. Conclusion

An optional parameter (?) may be left out and is undefined inside the function, so the body must check it, preferably with === undefined or ?? rather than a truthy check. A default parameter (= value) fills in a value when the argument is missing or undefined, but not when it is null. Optional and default parameters belong at the end of the parameter list, and a parameter cannot be both. Once a function has more than two or three optional values, an options object with destructured defaults keeps the calls readable.

9. References

The TypeScript Handbook documents the typing rules, and MDN documents the JavaScript behavior of default values and destructuring.

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.