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.
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:
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:
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:
const counter = createCounter();
console.log(counter.increment());
console.log(counter.increment());
// Output: 1
// Output: 2The 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:
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:
console.log(formatDate(2026, 7));
console.log(formatDate(2026, 7, "mid"));
// Output: 2026-7-1
// Output: 2026-7-midThe 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:
// 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:
optional();
withDefault();
// Output: undefined
// Output: UNTITLEDWith 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:
| Feature | Optional | Default |
|---|---|---|
| Type inside function | Type or undefined | Type only |
| Runtime value when omitted | undefined | The default value |
| Caller can pass undefined | Yes | Yes (triggers default) |
| Exists at runtime | No (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.
// 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:
// 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:
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:
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:
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:
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:
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:
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:
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:
// 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
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.
Frequently Asked Questions
Does TypeScript infer the type from a default value?
Can I use a default parameter before a required parameter?
What is the difference between optional and default parameters?
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.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.