learn
TypeScript

Generics

Reusable functions and interfaces with constraints and defaults.

Generics let one definition serve many types without surrendering checking. Interviewers ask for the one-line version first — "a placeholder the caller fills in" — then probe constraints and defaults.

Real-life situation

Every API response in your app has the same envelope (data plus status) but different payloads. Without generics you duplicate the interface per endpoint or fall back to any. With ApiResponse<T>, each call fills in its own T and keeps full checking — one definition, every endpoint typed.

Visual

Generic input to output
BrowserAPIRealtimeCacheDatabaseShareddurableephemeral

Try it

One envelope, many payloads

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
interface ApiResponse<T> {
data: T;
status: number;
}
function wrap<T>(value: T): ApiResponse<T> {
return { data: value, status: 200 };
}
function first<T>(items: T[]): T | undefined {
return items[0];
}
const names = wrap(["a", "b"]);
console.log(names.status);
console.log(first(names.data));
const n: number = wrap(5).data;
console.log(n + 1);
const broken: string = wrap(5).data;
console.log(broken);

Compiler not loaded. Check or Run loads it here (about 9 MB) — nothing downloads until you do.

Runner helpers

sleep(ms) waits; fakeFetch(url) returns deterministic sample data. TypeScript is stripped before running; imports and exports are not available in the sandbox.

Try fixing the last two lines to const broken: number = ... — clean. Notice wrap(["a", "b"]) needed no explicit type argument: inference fills T from the call. Constraints (T extends { id: number }) demand a minimum shape, and defaults (T = string) kick in when the caller says nothing.

Generics also carry interfaces (Box<T>), classes, and type aliases. The rule of thumb: generic over what varies per call, concrete about everything else.

Will it compile? — decide first, then reveal.

check.ts
function wrap<T>(value: T): T[] {
return [value];
}
const xs: number[] = wrap(1);
console.log(xs);

Interview questions

What is a generic in TypeScript?basic

A placeholder type you fill in at use time, written <T> like function first<T>(items: T[]): T. One definition then works for strings, users, or any shape while preserving the exact type through to the result. Without generics you would duplicate the function per type or fall back to any and lose safety. Read T as a variable for types: the caller chooses what it stands for on each call.

Interviewers also ask

  • How is a generic different from any?
How do you call a generic function?basic

You usually let inference fill the placeholder: first(['a', 'b']) resolves T to string with nothing extra to write. When inference fails or you want to be explicit, pass the argument in angle brackets like first<string>([]). Explicit arguments also pin an empty array to the type you intend. Prefer inference at call sites — it keeps code quiet and stays correct when element types evolve.

Interviewers also ask

  • When must you write the type argument explicitly?
How are built-in Array and Promise generic?basic

Array<T> stores whatever element type you choose, so string[] only accepts strings and returns strings from indexing. Promise<T> wraps the resolved value, so Promise<User> forces you to await a User. Both preserve the inner type through methods like map and then instead of degrading to any. Whenever you model a container or wrapper, follow the same pattern: parameterize the inside rather than fixing it.

Interviewers also ask

  • What does the T stand for in Array<T>?
How do generic constraints with extends work?intermediate

You bound the placeholder like <T extends { id: string }>, which requires every argument type to have at least those members. Inside the body you can then safely read item.id without narrowing. Constraints turn an anything-goes generic into one with a minimum shape. Keep them as small as the body needs — an over-wide constraint rejects valid callers for no reason.

Interviewers also ask

  • What can you safely access inside a constrained generic?
How do generic defaults work?intermediate

You provide a fallback like <T = string>, used when the caller omits the argument and inference finds nothing better. It makes a generic convenient without closing off other types — Box means Box<string> but Box<number> still works. Use defaults for the common case in library types and config objects. Keep the default compatible with your constraint, or the declaration itself becomes an error.

Interviewers also ask

  • When does the default apply instead of inference?
How do you write a generic interface?intermediate

You parameterize the whole shape, like interface Box<T> { value: T }, so each use picks its contents: Box<string> versus Box<number>. Methods inside can reuse T without redeclaring it. It suits wrappers, responses, and state containers where the shell is stable but the payload varies. Prefer one well-named parameter over several — extra parameters multiply the complexity every caller must manage.

Interviewers also ask

  • How would you type a generic API response wrapper?
Why is a generic safer than any?basic

any erases the type — inputs and outputs both become unchecked, so mistakes flow silently. A generic like <T>(x: T): T remembers the exact type from call to result, so passing a string guarantees a string back. You write the function once but every caller keeps full checking. Reach for generics whenever the shape varies but the relationship between input and output stays fixed.

Interviewers also ask

  • What does the compiler remember that any forgets?
What does keyof do in a generic?intermediate

keyof T is the union of T's property names, so <T, K extends keyof T> ties a key argument to the object it indexes. A helper like get(obj, key) then returns the precise property type instead of any, and misspelled keys become compile errors. It powers typed pickers, sorters, and form helpers. Use it whenever a string argument must name a real property of another argument.

Interviewers also ask

  • How does keyof prevent misspelled property names?

Key takeaways

  • T is filled per call — by inference first, explicitly when needed.
  • Constrain with extends to demand a shape; default with =.
  • One generic definition beats N copies or a silent any.

References

On this page