Generic Defaults & Inference

Generic defaults give a type parameter a fallback ( ) when none is provided, while type inference lets TypeScript figure out a generic's type from the arguments you pass — so you rarely write the angle brackets yourself.

Learn Generic Defaults & Inference in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Think of a coffee order. Inference is the barista guessing your milk from what you usually get — you don't say it, they just know. A default is the house standard when you don't specify ("regular oat milk unless you say otherwise"). And a const type parameter is the barista writing down your exact order word-for-word ("extra-hot, half-shot") instead of rounding it to "a coffee" — the precise details are kept.

1. Default Type Parameters

You can give a generic a default with . If the caller neither supplies T nor gives the compiler something to infer it from, T falls back to that default. Defaults shine on generic interfaces and classes — like — so simple callers don't have to spell out a type argument.

2. How Inference Works (and When to Annotate)

Most of the time you never write — TypeScript infers the type argument from the value you pass. identity("hi") infers T = string ; first([true, false]) infers the element type. You must annotate only when there's nothing to infer from — for example a function that returns T[] but takes no T argument.

3. const Type Parameters & Widening

By default, inference widens literals: pass ["red", "green"] and you get string[] , losing the exact values. A const type parameter ( , TypeScript 5.0) tells the compiler to keep the narrowest literal type — the precise tuple — without scattering as const across every call.

🎯 Your Turn

Mirror a generic default using a default parameter : greet() with no argument should fall back to "guest" . Fill in the two blanks marked ___ , then run it.

No blanks this time — just a brief and a starting outline. Build the helper yourself, run it, and check your output against the example in the comments.

Practice quiz

What is a default type parameter?

  • A runtime default argument
  • An interface
  • A fallback type for a generic, written like <T = string>
  • A const assertion

Answer: A fallback type for a generic, written like <T = string>. <T = string> is the type-level version of a default function parameter.

When does the default in '<T = string>' get used?

  • When the caller does not supply T and TypeScript cannot infer it from arguments
  • Always
  • Only with classes
  • Only in strict mode

Answer: When the caller does not supply T and TypeScript cannot infer it from arguments. If T can be inferred from an argument, the inferred type wins; the default applies only otherwise.

How does TypeScript usually figure out a generic's type argument?

  • You must always write <...>
  • From the tsconfig
  • Randomly
  • It infers it from the values you pass at the call site

Answer: It infers it from the values you pass at the call site. identity("hi") infers T = string and identity(7) infers T = number, so you rarely write <...>.

When MUST you annotate a type argument yourself?

  • Always

With no argument of type T, the compiler has nothing to read T from, so you write make<number>().

If you call 'function make<T>(): T[]' with no annotation, what is T?

  • unknown
  • string
  • never
  • any

Answer: unknown. With nothing to infer from and no default, T defaults to unknown.

By default, inference of '["red", "green"]' produces which type?

Inference widens literals, so you get string[] and lose the exact values.

What does a const type parameter '<const T>' (TS 5.0) do?

  • Freezes the array at runtime
  • Tells the compiler to infer the narrowest literal type instead of widening
  • Makes T a constant value
  • Requires 'as const' everywhere

Answer: Tells the compiler to infer the narrowest literal type instead of widening. <const T> keeps literal/tuple types, a cleaner alternative to scattering 'as const'.

Does '<const T>' change the runtime value of the array?

  • Yes, it freezes it
  • Yes, it copies it
  • Only in development
  • No — it only affects the inferred type

Answer: No — it only affects the inferred type. It changes only the type; the runtime array is exactly the same array.

Where must a defaulted type parameter appear relative to non-defaulted ones?

  • Before them
  • After them
  • Anywhere
  • Only alone

Answer: After them. <T = string, U> is invalid; defaulted type parameters must come after required ones.

On a generic interface, why add a default like 'ApiResponse<TData = unknown>'?

  • For speed
  • To enable inference of TData
  • So simple callers can omit the type argument
  • It is required syntax

Answer: So simple callers can omit the type argument. A default lets consumers write ApiResponse without spelling out the type argument.