Ch. 2 · TypeScript

TypeScript Generic Defaults and Parameter Order

Give type parameters defaults to make them optional, and order them so inference still works without explicit arguments.

~2 min readintermediateupdated Oct 5, 2026

A type parameter can carry a default, just like a function parameter: <T = string>. Defaults make a generic usable without spelling out every argument, but they interact with inference and ordering rules that decide whether callers must pass types explicitly.

Before you start

You should be comfortable with generic functions and constraints. This article covers defaults and ordering; it builds on basic generic syntax.

Step-by-step walkthrough

Step 1: Default the parameter that rarely changes

function createList<T = string>(items: T[]): T[] lets callers write createList(['a']) and get string[] without naming T. Defaults are most useful when one parameter is a sensible common case and the others are inferred from arguments.

Step 2: Order required before defaulted, and inferred before manual

Like function parameters, type parameters with defaults should follow those without. TypeScript also infers left to right, so a parameter that is inferred from an argument should not be blocked by one the caller must supply. Putting a manually specified parameter first is usually the safest order.

Step 3: Reset inference deliberately

A default is used only when the parameter cannot be inferred and is not provided. If the caller passes an argument, inference wins over the default. To force a default anyway, the caller must pass undefined or the type explicitly, so document when the default is the intended behavior.

Worked scenario

The default makes the type argument optional when the array is empty.

interface Result<T = string> {
  data: T;
  error: string | null;
}
const ok: Result = { data: 'hello', error: null }; // T defaults to string
const count: Result<number> = { data: 1, error: null };
void ok;
void count;
TypeScript

Walk through the example

Result without an argument defaults T to string, so data must be a string. Passing Result<number> overrides the default for that use. This is the pattern behind many library types, where the common payload type is the default and callers opt into something else only when needed.

Common mistake

Placing a defaulted parameter before a required one, which forces the required one to also have the same or a later default and produces confusing errors. Another mistake is expecting a default to override an inferred type: if the argument allows inference, the default is ignored.

Verify the behavior

Use the generic without a type argument and confirm the default applies. Pass an explicit type argument and confirm it overrides. Pass a value that infers a different type and confirm inference takes precedence. Finally, declare a defaulted parameter before a required one and read the compiler’s ordering error.

Interview exercise

Should function pair<K, V = string>(key: K, value: V) default V, or should the caller always pass it?

Answer and reasoning

Default V only if a string value is a genuinely common case; otherwise it hides intent and can silently type a value as string when the caller meant a number. Here V is inferred from value, so the default rarely applies — inference wins whenever a value is passed, and pair('a', 1) still infers number. Defaults belong on parameters that are typically not inferable.

Continue learning

Go deeper with generic constraints and const type parameters. Read the TypeScript generics handbook and try the TypeScript MCQs.

More in TypeScript

read ✓TypeScript · mid

TypeScript Const Type Parameters

Infer narrow literal and readonly types at call sites with const type parameters, and know the inference it does not change.

~3 min readread →
read ✓TypeScript · hard

TypeScript Abstract Classes and Contracts

Share behavior with abstract classes, enforce required members, and decide when an interface or composition is the better contract.

~2 min readread →
esc