Introduction to Generics (<T>)
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.
Deep Dive
1. Reusable Type Variables
Functions take arguments to handle reusable data values. Generics take type parameters (<T>) to handle reusable data types!
2. Why Generics Matter
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.
3. Generic Functions
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
4. Visual Generic Flow
[ Input Argument ] ──> "DevLoom" (string)
│
▼
[ Generic Parameter T ] ──> T = string
│
▼
[ Return Type T ] ──> Returns string with 100% type safety!
5. Generic Interfaces & Classes
interface ApiResponse<DataPayload> {
status: number;
data: DataPayload;
}
type UserData = { name: string; email: string };
type UserApiResponse = ApiResponse<UserData>;
6. Common Mistakes
- Using Generic T When Not Needed: Adding
<T>to a function that doesn't return or relateTto other parameters adds unnecessary complexity.
7. Next Steps
Use generics to build reusable containers and utilities. Next, let's explore Generic Constraints (extends)!
Next Up in TypeScript & Type System Mechanics
TypeScript Primitives & Explicit Type Annotations
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.
Type Inference & Literal Types (const vs. let)
`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.
Arrays, Tuples & Readonly Modifiers
`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.
