Yasin

Yasin

Typescript泛型概念

TypeScript 的泛型(Generics)是一种创建可重用、类型安全代码的机制。简单说,就是"类型的变量"

核心概念

泛型让你写代码时不指定具体类型,而是用一个"占位符",使用时再传入真正的类型。

// 不用泛型:只能处理 string
function getFirst(arr: string[]): string {
  return arr[0];
}

// 使用泛型:可以处理任何类型
function getFirst<T>(arr: T[]): T {
  return arr[0];
}

为什么需要泛型?

问题场景:

// 如果没有泛型,你需要为每种类型写一遍相同的逻辑
function getFirstString(arr: string[]): string {
  return arr[0];
}
function getFirstNumber(arr: number[]): number {
  return arr[0];
}
function getFirstUser(arr: User[]): User {
  return arr[0];
}
// ... 无穷无尽

泛型解决方案:

// 一个函数搞定所有类型
function getFirst<T>(arr: T[]): T {
  return arr[0];
}

// 使用时指定具体类型
const firstString = getFirst<string>(["a", "b", "c"]); // 返回 string
const firstNumber = getFirst<number>([1, 2, 3]); // 返回 number
const firstUser = getFirst<User>([user1, user2]); // 返回 User

泛型的几种用法

1. 函数泛型:

function identity<T>(value: T): T {
  return value;
}

const result = identity<number>(42); // result 是 number 类型

2. 接口泛型:

interface Container<T> {
  value: T;
  getValue(): T;
}

const stringContainer: Container<string> = {
  value: "hello",
  getValue() {
    return this.value;
  },
};

3. 类泛型:

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

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

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

const numberStack = new Stack<number>();
numberStack.push(1); // ✅ 正确
numberStack.push("a"); // ❌ 类型错误

4. React 组件中的泛型:

// 一个通用的列表组件
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// 使用
<List<User> items={users} renderItem={(user) => <span>{user.name}</span>} />;

泛型约束

有时你需要限制泛型的类型:

// T 必须有 length 属性
function logLength<T extends { length: number }>(arg: T): T {
  console.log(arg.length); // 现在安全了,T 肯定有 length
  return arg;
}

logLength("hello"); // ✅ string 有 length
logLength([1, 2, 3]); // ✅ array 有 length
logLength(123); // ❌ number 没有 length

类比理解

把泛型想象成:

  • 函数的参数function add(a, b) 中的 a, b 是值的占位符
  • 泛型参数function identity<T>(value: T) 中的 T 是类型的占位符