Type Parameters
Type parameters (like <T>) act as placeholders for types that are specified when the generic component is used. This enables type-safe code that works with multiple data types.
About 935 wordsAbout 12 min
2025-08-05
Note
Generic types enable the creation of reusable components that work with multiple types while maintaining type safety. They allow developers to write flexible, type-safe code without sacrificing the benefits of static typing.
Generic types provide a way to create components that work with any type while preserving type safety through type parameters.
Type Parameters
Type parameters (like <T>) act as placeholders for types that are specified when the generic component is used. This enables type-safe code that works with multiple data types.
Generic Functions
Functions can be made generic by adding type parameters, allowing them to work with different types while maintaining type safety. This eliminates the need to write separate functions for each type.
Generic Interfaces
Interfaces can use type parameters to define flexible contracts that work with multiple types. This is particularly useful for data structures and APIs.
Generic functions enable type-safe operations across different data types without code duplication.
Basic Generic Function
Generic functions use type parameters to work with multiple types while maintaining type safety.
function identity<T>(arg: T): T {
return arg;
}
// TypeScript infers the type based on usage
const num = identity(42); // Type is number
const str = identity("hello"); // Type is string
const bool = identity(true); // Type is boolean
// Explicit type specification
const explicitNum = identity<number>(42);Step 1
Multiple Type Parameters
Generic functions can have multiple type parameters for more complex scenarios.
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const numberStringPair = pair(42, "hello"); // [number, string]
const booleanDatePair = pair(true, new Date()); // [boolean, Date]Step 2
Generic Constraints
Type constraints limit what types can be used with generic functions, ensuring certain capabilities.
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(`Length: ${arg.length}`);
return arg;
}
logLength("hello"); // Works (strings have length)
logLength([1, 2, 3]); // Works (arrays have length)
logLength(42); // Error: number doesn't have length propertyStep 3
Generics work with interfaces and classes to create flexible, type-safe components.
Generic Interfaces Interfaces can use type parameters to define flexible contracts.
interface Collection<T> {
add(item: T): void;
remove(item: T): boolean;
contains(item: T): boolean;
size: number;
}
class ArrayCollection<T> implements Collection<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
remove(item: T): boolean {
const index = this.items.indexOf(item);
if (index > -1) {
this.items.splice(index, 1);
return true;
}
return false;
}
contains(item: T): boolean {
return this.items.includes(item);
}
get size(): number {
return this.items.length;
}
}Generic Classes Classes can be generic to work with multiple types while maintaining internal consistency.
class Box<T> {
private content: T;
constructor(initialContent: T) {
this.content = initialContent;
}
getContent(): T {
return this.content;
}
setContent(newContent: T): void {
this.content = newContent;
}
}
const numberBox = new Box(42);
const stringBox = new Box("hello");
const userBox = new Box({ name: "Alice", age: 30 });Generic Utility Types Generics enable powerful utility types for transforming other types.
type Optional<T> = {
[P in keyof T]?: T[P];
};
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface User {
name: string;
age: number;
email: string;
}
type PartialUser = Optional<User>;
type ReadonlyUser = Readonly<User>;Generics excel in real-world scenarios where type safety and flexibility are both required.
// Generic repository pattern
interface Repository<T> {
findById(id: number): T | null;
save(entity: T): void;
delete(id: number): boolean;
findAll(): T[];
}
class UserRepository implements Repository<User> {
private users: User[] = [];
findById(id: number): User | null {
return this.users.find(user => user.id === id) || null;
}
save(user: User): void {
const existingIndex = this.users.findIndex(u => u.id === user.id);
if (existingIndex >= 0) {
this.users[existingIndex] = user;
} else {
this.users.push(user);
}
}
delete(id: number): boolean {
const index = this.users.findIndex(user => user.id === id);
if (index >= 0) {
this.users.splice(index, 1);
return true;
}
return false;
}
findAll(): User[] {
return [...this.users];
}
}
// Generic event system
interface EventHandler<T> {
(event: T): void;
}
class EventEmitter<T> {
private handlers: EventHandler<T>[] = [];
on(handler: EventHandler<T>): void {
this.handlers.push(handler);
}
emit(event: T): void {
this.handlers.forEach(handler => handler(event));
}
off(handler: EventHandler<T>): void {
const index = this.handlers.indexOf(handler);
if (index > -1) {
this.handlers.splice(index, 1);
}
}
}TypeScript generics support sophisticated patterns for complex type scenarios:
// Conditional types
type ExtractString<T> = T extends string ? T : never;
// Mapped types with generics
type OptionalProperties<T> = {
[P in keyof T]?: T[P];
};
// Recursive generic types
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// Generic type guards
function isType<T>(value: any, type: new (...args: any[]) => T): value is T {
return value instanceof type;
}A type that is defined with a placeholder (type parameter) instead of specifying a concrete type, allowing code to be written once and used with different data types while maintaining type safety.
A placeholder for a type that is specified when a generic function or class is used, typically represented by single letters like T, U, or V following TypeScript conventions.
A restriction placed on type parameters that limits what types can be used with a generic component, ensuring the type has certain required properties or methods.
The TypeScript compiler's ability to automatically determine the type of a generic parameter based on the context in which it's used, eliminating the need for explicit type specification.
3d51f-feat: introducing generic types and propertieson Copyright Ownership:WARREN Y.F. LONG
License under:Attribution-NonCommercial-NoDerivatives 4.0 International (CC-BY-NC-ND-4.0)