实践笔记: TypeScript 中类、泛型的使用介绍和实践记录 |青训营

95 阅读4分钟

Typescript泛型基本介绍

TypeScript 中的泛型(Generics)是一种强大的特性,它允许在编写代码时使用一种抽象的类型,以后再具体指定这个类型。泛型可以用于函数、类、接口等,提供了更高的代码重用性和类型安全性。

软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。

「在像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。」

设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。

TypeScript可以使用泛型来创建可重用的组件。支持当前数据类型,同时也能支持未来的数据类型。扩展灵活。可以在编译时发现你的类型错误,从而保证了类型安全。

泛型的使用方法:

在函数、类、接口中使用泛型时,可以通过使用占位符(比如 T)来表示泛型类型。

当涉及到 TypeScript 中的泛型的使用方法时,有很多方面需要考虑,包括泛型函数、泛型类、泛型接口等。以下是全部涵盖泛型的基本使用方法示例:

// 泛型函数
function identity<T>(value: T): T {
  return value;
}

const resultNumber = identity<number>(42);
const resultString = identity<string>('Hello');

// 泛型类
class Container<T> {
  value: T;

  constructor(value: T) {
    this.value = value;
  }
}

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

// 泛型接口
interface Pair<K, V> {
  key: K;
  value: V;
}

const pairNumber: Pair<string, number> = { key: 'age', value: 25 };
const pairString: Pair<number, string> = { key: 123, value: 'Hello' };

// 泛型约束
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

const lengthResult = loggingIdentity('Hello'); // Output: 5

// 泛型参数默认值
function defaultValue<T = string>(value: T): T {
  return value;
}

const defaultResult1 = defaultValue<number>(42);
const defaultResult2 = defaultValue(); // 默认为 string 类型

// 泛型数组
function toArray<T>(value: T): T[] {
  return [value];
}

const numberArray = toArray<number>(42);
const stringArray = toArray<string>('Hello');

// 泛型函数类型
type GenericFunction<T> = (arg: T) => T;

const numberFunction: GenericFunction<number> = (x) => x * 2;
const stringFunction: GenericFunction<string> = (x) => x.toUpperCase();

上述代码中展示了 TypeScript 中涵盖了泛型函数、泛型类、泛型接口、泛型约束、泛型参数默认值、泛型数组和泛型函数类型等不同方面的使用方法。

泛型的使用场景:

  • 数据结构的抽象: 泛型允许你在编写通用的数据结构,如数组、栈、队列等,而不需要为不同的数据类型写多份重复的代码。
  • 类型安全性: 泛型提供了在编译时检查数据类型的机制,避免了在运行时出现类型错误。
  • 函数重用: 使用泛型函数可以处理多种类型的输入,减少了函数的重复定义。
  • 抽象函数的参数和返回类型: 泛型可以应用在函数的参数和返回值上,使函数更加通用。

使用类型约束增加代码安全性、灵活性:

类型约束在 TypeScript 中可以增加代码的安全性,它允许你在泛型中限制可接受的类型范围。下面是一个使用类型约束来限制输入类型的示例:

假设要创建一个函数,将数组中的元素连接成一个字符串,但只允许传入具有 .toString() 方法的元素。这样我们可以增加代码的安全性,确保只有支持转换为字符串的类型才能传递。

function joinElements<T extends { toString(): string }>(array: T[]): string {
  return array.map(item => item.toString()).join(', ');
}

const numberArray = [1, 2, 3];
const stringArray = ['Hello', 'World'];

console.log(joinElements(numberArray)); // Output: 1, 2, 3
console.log(joinElements(stringArray)); // Output: Hello, World

// 下面的代码会在编译时报错,因为布尔值没有 toString 方法
const booleanArray = [true, false];
console.log(joinElements(booleanArray)); // Compile Error

在上述代码中,函数 joinElements 使用了类型约束 T extends { toString(): string },它限制了传入的数组元素类型必须拥有 toString 方法,从而确保能够将元素转换为字符串。

通过使用类型约束,我们避免了不支持转换为字符串的类型被传入,从而增加了代码的安全性。同时,这种方法也增加了代码的灵活性,因为我们可以在函数内部使用 .toString() 方法,而不需要过多关心传入的类型是什么。

通过泛型和类型约束,可以在编写 TypeScript 代码时提高代码的安全性和可读性。合理的使用泛型可以使代码更具扩展性和适应性,减少错误和成本。

由此我们可以看出,使用泛型能够有效的增加我们代码的可复用性、安全性等等。泛型在我们使用typescript时是一个强大的工具。