Generics

A generic lets a function, class, or interface work with whatever type is handed to it, while still keeping full type checking — instead of writing the same logic once per type, or giving up and typing everything as any.

A generic function

TS firstItem.ts
function firstItem<T>(items: T[]): T {
  return items[0];
}

const firstNumber = firstItem([10, 20, 30]);
const firstName = firstItem(["Ana", "Ben"]);

console.log(firstNumber, firstName);
Output
10 Ana

<T> is a type parameter — a placeholder that gets filled in with whatever type is actually passed. TypeScript infers T as number for the first call and string for the second, and firstNumber/firstName come out correctly typed as a result, not as any.

Without generics: the any-typed version

TS anyVersion.ts
function firstItemAny(items: any[]): any {
  return items[0];
}

const result = firstItemAny([10, 20, 30]);
console.log(result.toUpperCase());
Output
Uncaught TypeError: result.toUpperCase is not a function

With any, the compiler has no idea result is really a number — calling a string method on it compiles just fine and only fails once the code actually runs. The generic version from the first example wouldn't have compiled a mistake like that at all.

Generic interfaces and constraints

TS box.ts
interface Box<T> {
  value: T;
}

function printLength<T extends { length: number }>(item: T): void {
  console.log(item.length);
}

const numberBox: Box<number> = { value: 42 };
printLength("hello");
printLength([1, 2, 3]);
Output
5
3

T extends { length: number } is a constraint — it restricts T to only types that have a .length property, so printLength(42) would fail to compile, but strings and arrays both qualify.

Note: just like every other TypeScript type, generic type parameters are erased at compile time — there is no way to check what T actually was at runtime, and calling something like typeof T inside a generic function isn't valid at all. If code genuinely needs to branch on the runtime type of a value, that has to come from checking the value itself (typeof value, value instanceof SomeClass), covered in the Type Narrowing lesson — never from inspecting the generic parameter directly.