S SmartDocs
系列: Typescript typescript 140 行 · 更新于 2025-12-27

01-generics.ts

Typescript/src/02-intermediate/01-generics.ts

// ============================================
// 01. Generics
// ============================================

// Basic Generic Function
function identity<T>(arg: T): T {
  return arg;
}

console.log(identity<string>("hello"));  // string
console.log(identity<number>(42));        // number
console.log(identity("hello"));           // TypeScript infers T as string

// Generic Array Function
function getFirst<T>(items: T[]): T | undefined {
  return items[0];
}

console.log(getFirst<string>(["a", "b", "c"]));     // string | undefined
console.log(getFirst<number>([1, 2, 3]));            // number | undefined
console.log(getFirst([true, false]));                 // TypeScript infers boolean

// Multiple Type Parameters
function pair<T, U>(first: T, second: U): [T, U] {
  return [first, second];
}

console.log(pair<string, number>("Alice", 25));      // [string, number]
console.log(pair<boolean, string>(true, "yes"));     // [boolean, string]

// Generic Constraints
interface HasLength {
  length: number;
}

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

logLength("hello");        // ✅ string has length
logLength([1, 2, 3]);      // ✅ array has length
// logLength(42);          // ❌ number doesn't have length

// Using Type Parameters in Constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person = { name: "Alice", age: 25 };
console.log(getProperty(person, "name"));  // string
console.log(getProperty(person, "age"));   // number
// getProperty(person, "email"); // ❌ Error: "email" is not a key

// Generic Interfaces
interface Box<T> {
  value: T;
}

const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 42 };

// Generic Interface with Methods
interface Repository<T> {
  findById(id: string): T | undefined;
  save(entity: T): void;
  delete(id: string): void;
}

interface User {
  id: string;
  name: string;
}

class UserRepository implements Repository<User> {
  private users: User[] = [];

  findById(id: string): User | undefined {
    return this.users.find(u => u.id === id);
  }
  
  save(entity: User): void {
    const index = this.users.findIndex(u => u.id === entity.id);
    if (index >= 0) {
      this.users[index] = entity;
    } else {
      this.users.push(entity);
    }
  }
  
  delete(id: string): void {
    this.users = this.users.filter(u => u.id !== id);
  }
}

// Generic Classes
class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }

  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2

const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");

// Generic Type Aliases
type Result<T> = {
  success: boolean;
  data?: T;
  error?: string;
};

type ApiResponse<T> = Promise<Result<T>>;

// Default Type Parameters
interface ApiResponse2<T = any> {
  data: T;
  status: number;
}

const response1: ApiResponse2 = { data: "anything", status: 200 };
const response2: ApiResponse2<User> = { data: { id: "1", name: "Alice" }, status: 200 };

export { identity, getFirst, pair, getProperty, Stack, UserRepository };

相关文章