Default Parameters in TypeScript

A default parameter provides a fallback value when the caller omits an argument. Learn how TypeScript infers the type from the default and how defaults differ from optional parameters.

5 min read

TypeScript default parameters give a function parameter a fallback value. When the caller omits the argument or passes undefined, TypeScript substitutes the default value at runtime. The syntax uses the equals sign after the parameter name, the same as JavaScript:

typescripttypescript
function greet(name: string = "Guest") {
  return `Hello, ${name}!`;
}
 
console.log(greet("Alice"));
console.log(greet());
// Output: Hello, Alice!
// Output: Hello, Guest!

The call with no argument uses the default value. The call with an argument overrides it. Both calls are valid, and the function always receives a string.

Type Inference From Defaults

TypeScript infers the parameter type from the default value, so you do not need to write the type annotation twice. The number below tells TypeScript that the parameter is a number, without any extra syntax:

typescripttypescript
function createCounter(start = 0) {
  let count = start;
  return {
    increment: () => ++count,
    reset: () => { count = start; return count; }
  };
}

Calling the returned methods twice in a row confirms the inferred type works correctly, since incrementing only makes sense for a number value:

typescripttypescript
const counter = createCounter();
console.log(counter.increment());
console.log(counter.increment());
// Output: 1
// Output: 2

The function body gets full type checking on the parameter without an explicit annotation, because the default value already told TypeScript what type to expect.

You can still add an explicit annotation when the default does not fully capture the intent or when you want a wider type:

typescripttypescript
function formatDate(
  year: number,
  month: number,
  day: number | string = 1
) {
  return `${year}-${month}-${day}`;
}

Calling this function with a number, or with a string in place of the day, both work because the annotation widens the accepted type beyond what the default alone would infer:

typescripttypescript
console.log(formatDate(2026, 7));
console.log(formatDate(2026, 7, "mid"));
// Output: 2026-7-1
// Output: 2026-7-mid

The annotation is wider than the default value, which would infer only a plain number on its own. Adding the annotation tells callers they can pass a string too.

Default Parameters Are Not Undefined Inside the Function

A key difference from optional parameters: a parameter with a default value does not include undefined in its type inside the function body. The default guarantees a value:

typescripttypescript
// Optional: type is string | undefined, must guard before use
function optional(title?: string) {
  console.log(title?.toUpperCase());
}
 
// Default: type is string, safe to use directly
function withDefault(title: string = "Untitled") {
  console.log(title.toUpperCase());
}

Calling both functions with no argument shows the practical difference: one prints undefined, the other always has a real string to work with:

typescripttypescript
optional();
withDefault();
// Output: undefined
// Output: UNTITLED

With the default parameter, calling a string method works without a guard. The default value eliminates the undefined case at runtime, and TypeScript reflects that in the type.

Default Parameters vs Optional Parameters

Both let callers omit the argument, but they have different types and different use cases:

FeatureOptionalDefault
Type inside functionType or undefinedType only
Runtime value when omittedundefinedThe default value
Caller can pass undefinedYesYes (triggers default)
Exists at runtimeNo (compile-time only)Yes (compiled to JS)
Best for"This might not apply""Use this fallback"

Choose a default parameter when the function always needs a concrete value to work with. Choose an optional parameter when a missing value has meaning in your logic.

typescripttypescript
// Default: always need a delimiter
function joinStrings(items: string[], separator = ", ") {
  return items.join(separator);
}

A timeout is different: most calls do not need one, and the function only acts on it when the caller explicitly provides a value, which is exactly what an optional parameter is for:

typescripttypescript
// Optional: timeout might not apply
function fetchWithTimeout(url: string, timeoutMs?: number) {
  if (timeoutMs !== undefined) {
    // Create an abort controller with a timeout
  }
}

Runtime Behavior

Default parameters are a JavaScript feature, not just a TypeScript annotation. The default value appears in the compiled output:

typescripttypescript
function multiply(a: number, b = 2): number {
  return a * b;
}

The type annotations disappear, but the default value stays because a modern JavaScript target supports it natively. This is what the compiled output looks like for the function above:

javascriptjavascript
function multiply(a, b = 2) {
  return a * b;
}

The default value survives compilation because it is real runtime behavior. This is different from type annotations and the optional-parameter question mark, both of which are erased during compilation.

When the caller passes undefined explicitly, the default still triggers. This matches the JavaScript specification:

typescripttypescript
function greet(name = "Guest") {
  return `Hello, ${name}!`;
}
 
console.log(greet(undefined));
// Output: Hello, Guest!

Passing undefined behaves the same as omitting the argument, because the default only activates for missing arguments and undefined. Passing null is a different story, and it is worth checking what happens if you try it:

typescripttypescript
console.log(greet(null));

The parameter's inferred type never included null, so the compiler stops this call before it can run and reports exactly which type was rejected:

texttext
Argument of type 'null' is not assignable to parameter of type 'string | undefined'.

In plain JavaScript, without TypeScript's type checking, this same call would run and print a greeting with the word "null" in it, because the default only skips undefined. TypeScript's strict null checking catches the mismatch before that surprising output ever happens.

Default Parameters With Complex Types

Defaults work with objects, arrays, and any type that can be expressed as a literal:

typescripttypescript
function createConfig(
  options: { theme?: string; debug?: boolean } = {}
) {
  const merged = {
    theme: options.theme ?? "light",
    debug: options.debug ?? false
  };
  return merged;
}

Calling this function with no argument, or with a partial options object, both produce a fully filled config because the merge step falls back to defaults for missing properties:

typescripttypescript
console.log(createConfig());
console.log(createConfig({ theme: "dark" }));
// Output: { theme: "light", debug: false }
// Output: { theme: "dark", debug: false }

The empty object default satisfies the parameter's object type. Inside the function, the parameter has no undefined union because the default guarantees an object, so accessing its properties needs no extra guard.

Common Mistakes

Confusing default parameters with optional parameters for callback types. The same callback rule from optional parameters applies: do not use a default in a callback type signature. Declare the parameter as required and let callers omit it if they do not need it.

Using a mutable default. A default value is evaluated once per call, not once per definition. This is fine for primitives, but an object or array default creates a new instance each call:

typescripttypescript
// Fine: primitives are immutable
function addItem(item: string, list: string[] = []) {
  list.push(item);
  return list;
}
 
console.log(addItem("a"));
console.log(addItem("b"));
// Output: ["a"]
// Output: ["b"]

Each call creates a new empty array because the default array expression runs every time the argument is omitted. The arrays are independent, which is the expected behavior.

Assuming the default annotation replaces a type annotation in all cases. When the default type is narrower than the intended type, add an explicit annotation, as shown in the formatDate example above.

For related concepts, see optional parameters in TypeScript and rest parameters in TypeScript.

Rune AI

Rune AI

Key Insights

  • A default parameter uses = value after the parameter name.
  • TypeScript infers the parameter type from the default value.
  • The parameter is optional at the call site but does not have undefined in its type.
  • The default value is a runtime feature; it exists in the compiled JavaScript.
  • Default parameters must come after all required parameters.
RunePowered by Rune AI

Frequently Asked Questions

Does TypeScript infer the type from a default value?

Yes. When you write name: string = "Guest", TypeScript infers the type as string from the default value. You can also write the type explicitly, but it is usually unnecessary when the default makes the type obvious.

Can I use a default parameter before a required parameter?

No. Like optional parameters, default parameters must come after all required parameters. Callers match arguments to parameters by position, so a default in the middle would be unreachable.

What is the difference between optional and default parameters?

An optional parameter (?) has type Type | undefined and is undefined when omitted. A default parameter (= value) has the type of the value and receives the default when omitted. The default parameter's type does not include undefined.

Conclusion

Default parameters let you assign fallback values that kick in when the caller omits an argument. TypeScript infers the type from the default, so you rarely need an explicit annotation. The parameter becomes optional at the call site, but its type inside the function does not include undefined. Place defaults after required parameters, and choose defaults over optional parameters when you always want a concrete value to work with.