TypeScript 类、泛型的使用实践记录 | 青训营

56 阅读4分钟

TypeScript中的泛型是一种在编写代码时能够适应多种数据类型的方法。它可以增加代码的灵活性和安全性,因为它能够对参数类型进行约束和检查。

1.泛型类:
在TypeScript中,可以使用泛型来定义一个类,以适应不同类型的数据。例如,假设我们有一个通用的数据存储类,我们可以使用泛型来定义它:

    class DataStorage<T> {
  private data: T[] = [];
  addItem(item: T) {
    this.data.push(item);
  }
  removeItem(item: T) {
    const index = this.data.indexOf(item);
    if (index !== -1) {
      this.data.splice(index, 1);
    }
  }
  getItems(): T[] {
    return [...this.data];
  }
}

上面的DataStorage类使用了一个泛型T来定义数据类型。这个类有三个方法:addItem用于添加数据,removeItem用于删除数据,getItems用于获取所有数据。这样,我们就可以根据不同的数据类型来创建不同的DataStorage实例。

const stringStorage = new DataStorage<string>();
stringStorage.addItem("Apple");
stringStorage.addItem("Banana");
stringStorage.addItem("Orange");
console.log(stringStorage.getItems()); // ["Apple", "Banana", "Orange"]

const numberStorage = new DataStorage<number>();
numberStorage.addItem(1);
numberStorage.addItem(2);
numberStorage.addItem(3);
console.log(numberStorage.getItems()); // [1, 2, 3]

2.泛型函数:
除了泛型类,我们还可以使用泛型来定义函数。这样我们就可以创建能够适应多种参数类型的函数。例如,假设我们有一个函数用于打印数组中的所有元素:
    function printArray<T>(array: T[]): void {
  for (let item of array) {
    console.log(item);
  }
}

上面的printArray函数使用了一个泛型T来定义参数的类型。这样,我们就可以使用这个函数来处理不同类型的数组。

printArray<string>(["Apple", "Banana", "Orange"]); // Apple Banana Orange
printArray<number>([1, 2, 3]); // 1 2 3

3.类型约束:
有时候,我们希望对泛型进行一些约束,以限制可用的类型。这可以通过使用extends关键字和类型约束来实现。例如,我们可以定义一个泛型函数,要求传入的参数必须有length属性:

  function printLength<T extends { length: number }>(value: T): void {
  console.log(value.length);
}

在上面的例子中,我们使用了extends { length: number }来约束了泛型T必须要有length属性。这样,我们就可以确保传入的参数是一个具有length属性的对象。

printLength("Hello"); // 5
printLength([1, 2, 3]); // 3
printLength({ length: 4, name: "John" }); // 4
printLength(123); // 编译错误,数字类型没有length属性

通过类型约束,我们可以在编写代码时更加灵活和安全。它可以帮助我们避免一些常见的错误,提高代码的可靠性。

综上所述,TypeScript中的泛型是一种非常有用的特性,它可以增加代码的灵活性和安全性。我们可以使用泛型类和泛型函数来处理不同类型的数据,并使用类型约束来对泛型进行约束和检查。

下面我来讨论一些使用泛型的实践记录和场景。

1.类中使用泛型:
在类中使用泛型可以增强类的通用性。例如,你可以创建一个容器类来处理各种类型的数据。这样一来,用户可以在实例化这个容器类时指定数据的类型,并且在类的方法中获取和操作这些数据。

  class Container<T> {
  private data: T;
  constructor(initialData: T) {
    this.data = initialData;
  }
 getData(): T {
    return this.data;
  }
  setData(newData: T): void {
    this.data = newData;
  }
}
const container = new Container<number>(10);
console.log(container.getData()); // 输出: 10 
container.setData(20);
console.log(container.getData()); // 输出: 20

2.函数中使用泛型:
在函数中使用泛型可以处理多种类型的参数和返回值。这非常有用,例如在处理数组时,我们可以编写一个通用的函数来对数组中的元素进行操作。

  function reverseArray<T>(arr: T[]): T[] {
  return arr.reverse();
}
const stringArray = ["a", "b", "c"];
const reversedStringArray = reverseArray<string>(stringArray);
console.log(reversedStringArray); // 输出: ["c", "b", "a"]
const numberArray = [1, 2, 3];
const reversedNumberArray = reverseArray<number>(numberArray);
console.log(reversedNumberArray); // 输出: [3, 2, 1]

3.类型约束:
有时候我们需要限制泛型的类型范围,在 TypeScript 中可以使用类型约束来实现。例如,我们可以使用 extends 关键字来约束泛型类型必须是某个接口的实现。

  interface Lengthy {
  length: number;
}
function printLength<T extends Lengthy>(input: T): void {
 console.log(input.length);
}
printLength("hello"); // 输出: 5
printLength([1, 2, 3]); // 输出: 3

在上面的例子中,printLength 函数接收一个泛型参数,并使用 extends Lengthy 约束泛型类型必须包含 length 属性。这样,我们可以在函数中使用 input.length 获取泛型参数的长度,而不论参数的具体类型是什么。