TypeScript 类、泛型的使用实践记录:探讨TypeScript中的泛型的使用方法和场景,以及如何使用类型约束来增加代码的灵活性和安全性;| 青训营

81 阅读3分钟

这是昨天的续作.

泛型允许我们编写可重用的组件,并在不同类型之间进行类型安全的操作。

关于此次笔记分析和探讨,我思考了很久,感觉零零散散的,于是便查阅了一些资料.

1. 类中的泛型参数:

```class Container<T> {
  private item: T;
  
  constructor(item: T) {
    this.item = item;
  }
  
  getItem(): T {
    return this.item;
  }
}

const numberContainer = new Container<number>(42);
const stringContainer = new Container<string>("Hello");

console.log(numberContainer.getItem()); // 输出: 42
console.log(stringContainer.getItem()); // 输出: "Hello"

这个例子表达了,Container 类接受一个泛型参数 T,并将其用作 item 属性的类型和 getItem 方法的返回类型。通过传入不同的类型参数,可以创建具有不同类型的容器实例。

2.泛型函数:

```function printArray<T>(array: T[]): void {
  for (let item of array) {
    console.log(item);
  }
}

const numbers: number[] = [1, 2, 3, 4, 5];
const strings: string[] = ["apple", "banana", "orange"];

printArray(numbers); // 输出: 1 2 3 4 5
printArray(strings); // 输出: "apple" "banana" "orange"

这个泛型函数中 printArray 函数是一个泛型函数,它接受一个数组作为参数,并打印出数组中的每个元素。通过使用泛型参数 T,可以传入不同类型的数组,并保持类型安全。

3.类型约束:

```interface Lengthwise {
  length: number;
}

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

const stringObj = { length: 5, value: "Hello" };
const arrayObj = [1, 2, 3];

printLength(stringObj); // 输出: 5
printLength(arrayObj); // 输出: 3

这个类型约束中,定义了一个接口 Lengthwise,它具有一个 length 属性。然后,使用 extends 关键字将泛型参数 T 约束为实现了 Lengthwise 接口的类型。这样,可以确保传递给 printLength 函数的参数具有 length 属性,从而增加代码的灵活性和安全性。

  1. 泛型函数和类型约束:
```function mergeArrays<T>(arr1: T[], arr2: T[]): T[] {
  return [...arr1, ...arr2];
}

const numbers: number[] = [1, 2, 3];
const strings: string[] = ["apple", "banana", "orange"];

const mergedNumbers: number[] = mergeArrays(numbers, [4, 5, 6]);
const mergedStrings: string[] = mergeArrays(strings, ["grape", "melon"]);

console.log(mergedNumbers); // 输出: [1, 2, 3, 4, 5, 6]
console.log(mergedStrings); // 输出: ["apple", "banana", "orange", "grape", "melon"]

这个例子中,mergeArrays 函数接受两个相同类型的数组,并返回一个合并后的数组。通过使用泛型参数 T,我们可以在函数调用时传入不同类型的数组,而函数内部的逻辑仍然保持类型安全。

5.2. 泛型类和类型约束:

```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(2);
numberStack.push(3);

console.log(numberStack.pop()); // 输出: 3
console.log(numberStack.pop()); // 输出: 2

const stringStack = new Stack<string>();
stringStack.push("apple");
stringStack.push("banana");
stringStack.push("orange");

console.log(stringStack.pop()); // 输出: "orange"
console.log(stringStack.pop()); // 输出: "banana"

其中Stack 类是一个泛型类,可以用于创建不同类型的栈。通过在实例化时指定类型参数,我们可以创建特定类型的栈,并在栈的操作中保持类型安全。

6.泛型接口和类型约束:

```interface Calculator<T> {
  add(a: T, b: T): T;
  subtract(a: T, b: T): T;
}

const numberCalculator: Calculator<number> = {
  add(a, b) {
    return a + b;
  },
  subtract(a, b) {
    return a - b;
  },
};

console.log(numberCalculator.add(5, 3)); // 输出: 8
console.log(numberCalculator.subtract(10, 4)); // 输出: 6

const stringCalculator: Calculator<string> = {
  add(a, b) {
    return `${a} ${b}`;
  },
  subtract(a, b) {
    throw new Error("Operation not supported");
  },
};

console.log(stringCalculator.add("Hello", "World")); // 输出: "Hello World"

其中Calculator 接口是一个泛型接口,定义了加法和减法操作。通过在接口实现时指定类型参数,我们可以创建特定类型的计算器,并在操作中保持类型安全。

在学习中,我们应当注意,首先,确保对泛型和类型约束的基本概念有一个清晰的理解。了解什么是泛型,它们如何在函数、类和接口中使用,以及如何使用类型约束来限制泛型参数的行为。TypeScript 的官方文档是学习和理解泛型和类型约束的良好资源。浏览官方文档中关于泛型和类型约束的章节,并尝试运行示例代码来加深理解。尝试编写一些简单的函数、类或接口,使用泛型和类型约束来解决具体问题。通过实际的编码练习,将更深入地理解泛型的使用方法和场景,并更好地掌握类型约束的作用。