Generics let you write functions, interfaces, and classes that operate on multiple types while preserving full type safety. A generic identity function written as function identity<T>(arg: T): T { return arg; } takes a value of any type and returns a value of exactly the same type, with TypeScript filling in T from context. This pattern is the foundation of reusable container types like Array<T>.
When a generic must guarantee certain capabilities, you constrain it with extends. For example, function longest<T extends { length: number }>(a: T, b: T): T ensures that any type passed in has a numeric length property, letting the function compare values safely. Generic interfaces apply the same idea at a structural level: interface Box<T> { contents: T; } describes a container whose contents vary, while generic classes like class Stack<T> with push(item: T) and pop(): T | undefined define data structures that track element types through every operation.