VVLL.net

Generics(泛型)

日期:2024-08-22 09:58:31

TypeScript 泛型是一种强大的工具,它允许你创建可复用的组件,适用于多种数据类型,而无需在每次使用时指定具体类型。泛型使代码更加灵活和可扩展。以下是对 TypeScript 泛型的详细介绍,包括泛型变量、泛型类型、泛型类、泛型约束及学习技巧。

1. 泛型概述

泛型(Generics)允许定义函数、类或接口时不指定具体类型,而是在使用时再指定具体类型。这种方式使得代码在保持类型安全的同时,具有更高的灵活性。

2. 泛型变量

泛型变量用来捕获传递给函数、类或接口的类型。

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

let output1 = identity<string>("Hello");
let output2 = identity<number>(123);
  • identity<T>(arg: T): T 中的 T 就是泛型变量。
  • 调用时指定具体类型,例如 identity<string>("Hello")

3. 泛型类型

泛型类型用于定义包含一个或多个类型参数的类型。

type GenericIdentityFn<T> = (arg: T) => T;

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

let myIdentity: GenericIdentityFn<number> = identity;
  • type GenericIdentityFn<T> = (arg: T) => T 定义了一个泛型类型别名。
  • let myIdentity: GenericIdentityFn<number> = identity 使用泛型类型别名定义变量。

4. 泛型类

泛型类允许类在实例化时指定类型。

class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;

  constructor(zeroValue: T, add: (x: T, y: T) => T) {
    this.zeroValue = zeroValue;
    this.add = add;
  }
}

let myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 5));  // 输出 5
  • class GenericNumber<T> 定义了一个泛型类。
  • new GenericNumber<number>(0, (x, y) => x + y) 实例化时指定具体类型。

5. 泛型约束

泛型约束用于限制泛型类型的范围。

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);  // 现在可以访问length属性
  return arg;
}

loggingIdentity({ length: 10, value: 3 });
  • T extends Lengthwise 表示泛型类型 T 必须满足 Lengthwise 接口的约束。
  • 这样可以确保 T 类型具有 length 属性。

6. 学习技巧

要掌握 TypeScript 泛型,可以采用以下学习技巧:

  1. 从简单示例入手:理解基本概念后,再逐步复杂化。
  2. 阅读官方文档:TypeScript 官方文档是最权威的学习资源,涵盖了泛型的方方面面。
  3. 实践练习:多写代码,尝试将常见的非泛型代码改写成泛型代码。
  4. 阅读开源项目代码:学习他人如何在大型项目中使用泛型。
  5. 使用TypeScript Playground:在TypeScript Playground 上实验代码,快速验证想法。

综合示例

以下是一个综合示例,展示了如何使用泛型函数、泛型接口和泛型类:

// 泛型函数
function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

const mergedObj = merge({ name: "Alice" }, { age: 25 });
console.log(mergedObj.name, mergedObj.age);

// 泛型接口
interface Result<T> {
  success: boolean;
  data: T;
}

function fetchData<T>(url: string): Result<T> {
  // 模拟请求
  const data: T = JSON.parse(`{"data": "example"}`);
  return { success: true, data };
}

const result = fetchData<{ message: string }>("http://example.com");
console.log(result.data.message);

// 泛型类
class DataStorage<T> {
  private items: T[] = [];

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

  removeItem(item: T) {
    this.items = this.items.filter(i => i !== item);
  }

  getItems(): T[] {
    return [...this.items];
  }
}

const textStorage = new DataStorage<string>();
textStorage.addItem("Alice");
textStorage.addItem("Bob");
textStorage.removeItem("Alice");
console.log(textStorage.getItems());

通过这些示例,可以全面理解 TypeScript 中泛型的使用和优势。多加练习,可以更好地掌握和应用泛型,使代码更加通用和可复用。

参考

标签