返回

运用泛型提升JavaScript,以类型安全方式操作数据

前端

泛型是TypeScript最强大的功能之一,它允许您创建可重用且灵活的代码。在本文中,我们将探讨如何使用泛型来创建可重用且灵活的代码。

什么是泛型?

泛型是一个占位符,允许您在代码中使用任何类型。例如,您可以定义一个名为List的泛型,它可以存储任何类型的元素。

class List<T> {
  private data: T[];

  constructor(...items: T[]) {
    this.data = items;
  }

  add(item: T) {
    this.data.push(item);
  }

  get(index: number): T {
    return this.data[index];
  }

  size(): number {
    return this.data.length;
  }
}

上面的代码定义了一个泛型类List。该类可以存储任何类型的元素。您可以在实例化该类时指定元素的类型。例如,您可以创建一个存储字符串的List如下:

const list = new List<string>("Hello", "World");

现在,您可以使用该列表就像使用任何其他JavaScript数组一样。

console.log(list.get(0)); // Hello
console.log(list.size()); // 2

泛型不仅可以用于类,还可以用于函数和接口。例如,您可以定义一个名为map的泛型函数,它将一个数组中的每个元素映射到一个新值。

function map<T, U>(array: T[], f: (item: T) => U): U[] {
  return array.map(f);
}

上面的代码定义了一个泛型函数map。该函数将一个数组中的每个元素映射到一个新值。您可以使用该函数来将一个字符串数组映射到一个数字数组,如下所示:

const numbers = map(["1", "2", "3"], (item: string) => parseInt(item));

现在,numbers变量将包含一个数字数组:[1, 2, 3]。

泛型的类型

泛型可以是任何类型,包括类、接口、函数和原始类型。例如,您可以定义一个名为Box的泛型类,它可以存储任何类型的元素。

class Box<T> {
  private data: T;

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

  get(): T {
    return this.data;
  }
}

上面的代码定义了一个泛型类Box。该类可以存储任何类型的元素。您可以实例化该类并传入任何类型的元素。例如,您可以创建一个存储字符串的Box如下:

const box = new Box("Hello World");

现在,您可以使用该box变量就像使用任何其他JavaScript变量一样。

console.log(box.get()); // Hello World

泛型还可以用于接口。例如,您可以定义一个名为Printable的泛型接口,它表示一个可打印的类型。

interface Printable<T> {
  print(): T;
}

上面的代码定义了一个泛型接口Printable。该接口表示一个可打印的类型。您可以创建任何类型的Printable实现。例如,您可以创建一个打印字符串的Printable实现,如下所示:

class StringPrintable implements Printable<string> {
  private data: string;

  constructor(data: string) {
    this.data = data;
  }

  print(): string {
    return this.data;
  }
}

上面的代码定义了一个StringPrintable类,它实现了Printable接口。该类打印一个字符串。您可以实例化该类并打印一个字符串,如下所示:

const printable = new StringPrintable("Hello World");
console.log(printable.print()); // Hello World

泛型还可以用于函数。例如,您可以定义一个名为map的泛型函数,它将一个数组中的每个元素映射到一个新值。

function map<T, U>(array: T[], f: (item: T) => U): U[] {
  return array.map(f);
}

上面的代码定义了一个泛型函数map。该函数将一个数组中的每个元素映射到一个新值。您可以使用该函数来将一个字符串数组映射到一个数字数组,如下所示:

const numbers = map(["1", "2", "3"], (item: string) => parseInt(item));

现在,numbers变量将包含一个数字数组:[1, 2, 3]。

泛型的约束

泛型类型可以被约束为只能是某些类型的元素。例如,您可以定义一个名为List的泛型类,它只能存储字符串元素。

class List<T extends string> {
  private data: T[];

  constructor(...items: T[]) {
    this.data = items;
  }

  add(item: T) {
    this.data.push(item);
  }

  get(index: number): T {
    return this.data[index];
  }

  size(): number {
    return this.data.length;
  }
}

上面的代码定义了一个泛型类List。该类只能存储字符串元素。您可以实例化该类并传入一个字符串数组。例如,您可以创建一个存储字符串的List如下:

const list = new List<string>("Hello", "World");

现在,您可以使用该列表就像使用任何其他JavaScript数组一样。

console.log(list.get(0)); // Hello
console.log(list.size()); // 2

泛型约束还可以用于函数和接口。例如,您可以定义一个名为map的泛型函数,它将一个数组中的每个元素映射到一个新值,并且该新值必须是字符串类型。

function map<T extends string, U extends string>(array: T[], f: (item: T) => U): U[] {
  return array.map(f);
}

上面的代码定义了一个泛型函数map。该函数将一个数组中的每个元素映射到一个新值,并且该新值必须是字符串类型。您可以使用该函数来将一个字符串数组映射到一个字符串数组,如下所示:

const numbers = map(["1", "2", "3"], (item: string) => item.toUpperCase());

现在,numbers变量将包含一个字符串数组:["1", "2", "3"]。