← TypeScript EnglishChapter 06 of 12

Generics

## Learning Objectives - Create generic functions - Define generic classes - Use generic interfaces - Apply type constraints - Master utility types ## Why Generics? ### Without Generics ```typescript function identity(arg: any): any { return arg; } let num = identity(42); // type: any let str = identity("hello"); // type: any // Lost type information ``` ### With Generics ```typescript function identity(arg: T): T { return arg; } let num = identity(42); // type: number let str = identity("hello"); // type: string // Type information preserved ``` ## Generic Functions ### Syntax ```typescript function firstElement(arr: T[]): T | undefined { return arr[0]; } let num = firstElement([1, 2, 3]); // number | undefined let str = firstElement(["a", "b", "c"]); // string | undefined ``` ### Multiple Type Parameters ```typescript function pair(first: T, second: U): [T, U] { return [first, second]; } let p = pair("hello", 42); // [string, number] ``` ## Generic Interfaces ### Declaration ```typescript interface Container { value: T; getValue(): T; } class Box implements Container { constructor(public value: T) {} getValue(): T { return this.value; } } let stringBox = new Box("hello"); let numBox = new Box(42); ``` ### Generic Interface Multiple Type Parameters ```typescript interface Dictionary { key: T; value: U; } let dict: Dictionary = { key: "age", value: 30 }; ``` ## Generic Classes ### Basic Syntax ```typescript class DataStore { private data: T[] = []; add(item: T): void { this.data.push(item); } getAll(): T[] { return [...this.data]; } getByIndex(index: number): T | undefined { return this.data[index]; } } let stringStore = new DataStore(); stringStore.add("hello"); stringStore.add("world"); ``` ### Generic Class Multiple Type Parameters ```typescript class Pair { constructor( public key: K, public value: V ) {} toString(): string { return `${this.key}: ${this.value}`; } } let pair = new Pair("age", 30); pair.toString(); // "age: 30" ``` ## Generic Constraints ### Using extends ```typescript interface HasLength { length: number; } function logLength(arg: T): T { console.log(arg.length); return arg; } logLength("hello"); // 5 logLength([1, 2, 3]); // 3 logLength({ length: 10 }); // 10 // logLength(123); // Error: number has no length ``` ### Constrain to Object Keys ```typescript function getProperty(obj: T, key: K): T[K] { return obj[key]; } let person = { name: "Alice", age: 30, city: "NYC" }; getProperty(person, "name"); // string getProperty(person, "age"); // number // getProperty(person, "foo"); // Error ``` ### Multiple Constraints ```typescript function compare( a: T, b: T ): number { return a.name.localeCompare(b.name) || a.age - b.age; } interface HasName { name: string; } interface HasAge { age: number; } ``` ## Generic Type Aliases ```typescript type Result = { success: boolean; data?: T; error?: string; }; function fetchUser(): Result { return { success: true, data: user }; } ``` ## Default Type Parameters ```typescript interface Response { data: T; status: number; } let r1: Response = { data: "OK", status: 200 }; let r2: Response = { data: 42, status: 200 }; ``` ## Generic Utility Types ### Partial All properties optional: ```typescript interface User { name: string; age: number; } function updateUser(id: number, updates: Partial): User { // Merge updates with existing user return { ...existingUser, ...updates }; } ``` ### Required All properties required: ```typescript interface Config { host?: string; port?: number; } let partial: Partial = { host: "localhost" }; let full: Required = { host: "localhost", port: 8080 }; ``` ### Pick Select properties: ```typescript interface User { id: number; name: string; email: string; age: number; } type UserPreview = Pick; // { id: number; name: string } ``` ### Omit Exclude properties: ```typescript type UserWithoutEmail = Omit; // { id: number; name: string; age: number } ``` ### Record Create object type: ```typescript type Role = "admin" | "user" | "guest"; type Permissions = Record; let perms: Permissions = { admin: true, user: false, guest: true }; ``` ### Exclude Exclude from union: ```typescript type T = "a" | "b" | "c"; type U = Exclude; // "b" | "c" ``` ### Extract Extract from union: ```typescript type T = "a" | "b" | "c"; type U = Extract; // "a" | "b" ``` ### NonNullable Remove null and undefined: ```typescript type T = string | null | undefined; type U = NonNullable; // string ``` ### ReturnType Get function return type: ```typescript function createUser() { return { name: "Alice", age: 30 }; } type User = ReturnType; // { name: string; age: number } ``` ### Parameters Get function parameters: ```typescript function greet(name: string, age: number): string { return `Hello, ${name}, age ${age}`; } type GreetParams = Parameters; // [name: string, age: number] ``` ## Generic with new() ```typescript function create(constructor: new () => T): T { return new constructor(); } class User { name: string = "Default"; } let user = create(User); ``` ## Generic in Conditional Types ```typescript type IsArray = T extends any[] ? true : false; type A = IsArray; // true type B = IsArray; // false ``` ## Summary - Generics preserve type information - `` is the most common type parameter name - Use `extends` to constrain generic types - Generic functions: `function foo(x: T): T` - Generic interfaces: `interface Container` - Generic classes: `class Box` - Built-in utility types: Partial, Pick, Omit, Record - Use multiple type parameters: ``

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →