Quick Overview
A generic passes the caller's type through the function instead of erasing it, so `identity(5)` returns `number` rather than `any`. That is the difference between reusable and untyped.
Functions take arguments to handle reusable data values. Generics take type parameters (<T>) to handle reusable data types!
Without generics, you either have to duplicate code for every data type or use any (which destroys type safety). Generics capture the exact argument type and pass it through to return values.
Use angle brackets (<T>) to define a generic type parameter:
// Generic identity function: T captures input type
function identity<T>(arg: T): T {
return arg;
}
const numResult = identity<number>(42); // Explicit T = number
const strResult = identity("DevLoom"); // Inferred T = string (Hovering shows string!)
// Generic Array Helper
function getFirstItem<T>(list: T[]): T | undefined {
return list[0];
}
const firstNumber = getFirstItem([10, 20, 30]); // Inferred return type: number | undefined
const firstString = getFirstItem(["a", "b"]); // Inferred return type: string | undefined
[ Input Argument ] ──> "DevLoom" (string)
│
▼
[ Generic Parameter T ] ──> T = string
│
▼
[ Return Type T ] ──> Returns string with 100% type safety!
interface ApiResponse<DataPayload> {
status: number;
data: DataPayload;
}
type UserData = { name: string; email: string };
type UserApiResponse = ApiResponse<UserData>;
<T> to a function that doesn't return or relate T to other parameters adds unnecessary complexity.Use generics to build reusable containers and utilities. Next, let's explore Generic Constraints (extends)!
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.