TSworkingts-evergreen

Generics, the basics

One function, every type.

Generics let a function, class, or type take a type as a parameter β€” so you write identity<T> once and it stays type-safe for strings, numbers, and your own interfaces alike.

What a type parameter buys you

A generic introduces a type variable β€” written in angle brackets, conventionally T β€” that stands in for a type the caller supplies. The compiler infers it from the arguments, so identity('hi') returns string and identity(42) returns number from the same definition. Compare that to any, which throws the type away: a generic carries it through, linking the input type to the output type.

Constraints with extends

An unconstrained T could be anything, so you can only do to it what you can do to every value. Writing T extends Constraint narrows what callers may pass and unlocks the matching properties inside the body. T extends { length: number } lets you read .length; K extends keyof T ties one parameter to the keys of another, which is how a type-safe property getter works.

Default type parameters

A type parameter can declare a default with =, used when the caller neither passes one explicitly nor lets inference fill it in. Container<T = string> means Container (no brackets) behaves like Container<string>, while Container<number> still works. Defaults keep common cases terse without losing the escape hatch.

Generic classes

Classes take type parameters too. A Box<T> declared once stores and returns a T with full type safety β€” new Box(0) infers Box<number>, and its .value is a number. The type parameter is in scope across every field and method of the class.

Try it 6 examples

identity<T>: the canonical generic

TSintro
Press Run to execute

The values print as hello 42 true and their runtime tags as string number boolean β€” proof the single identity definition preserved each distinct type rather than collapsing them to any.