解密 TypeScript 泛型:从0到1快速入门

96 阅读4分钟

1 在Typecript 中什么是泛型

泛型是 TypeScript 中非常重要的概念之一,它允许我们在定义函数、类和接口时使用一个或多个类型参数来表示类型的未知部分,从而增加代码的复用性和灵活性。

简单来说,泛型就是一种参数化的类型,可以在函数或类定义时指定一个或多个类型参数,这些参数可以用来定义函数参数类型、返回值类型、类成员类型等。例如:

function mingslFn<T>(arg: T): T {
  return arg;
}
​
let result = mingslFn<string>('hello'); // T 的类型为 string
console.log(result);//"hello"

在这个例子中,mingslFn 函数使用泛型 <T> 来表示参数类型和返回值类型,这样就可以让函数接受任意类型的参数并返回相同类型的结果。

在使用泛型时,可以使用任意标识符作为泛型参数名,例如 TUV 等,一般使用单个大写字母表示泛型参数,以便和普通变量进行区分。

总之,泛型是 TypeScript 中非常重要的概念,它可以大大提高代码的复用性和灵活性,特别是在处理复杂数据结构或接口时,可以让代码更加简洁、易读和可维护。

2 在TypeScript 中使用泛型接口

在 TypeScript 中,我们可以使用泛型接口来定义一个可以接受不同类型参数的接口。泛型接口可以让我们在编写代码时决定使用哪些类型作为参数。

下面是一个示例,演示如何在 TypeScript 中定义和使用泛型接口:

interface DataResult<T> {
  code: number;
  data: T;
  message:string;  
}
interface Page{
  totalElements:number,
  size:number,
  content:Product[]
}
interface Product{
  id:number,
  name:string,
  price:number
}
/**
 * 返回商品的分页数据
 */
let result:DataResult<Page>={
  code:0,
  data:{
    totalElements:2,
    size:10,
    content:[
      {
        id:1,
        name:'TS',
        price:9.99
​
      },
      {
         id:2,
        name:'JS',
        price:9.99
      }
    ]
  },
  message:'Success'
}
console.log(result);
​
/**
 * 返回单个商品数据
 */
let result1:DataResult<Product>={
  code:0,
  message:'Success',
  data:{
        id:1,
        name:'TS',
        price:9.99
​
      }
}
console.log(result1);

使用泛型接口可以让代码更加灵活和通用,能够处理不同类型的数据,并且可以提高代码的复用性。

3 在TypeScript 中使用泛型类

在 TypeScript 中,泛型类可以帮助我们实现具有通用性的代码,尤其是在编写数据结构或算法的时候,使用泛型类可以减少代码的重复性。

以下是实战一个通用队列,展示了如何使用泛型类来实现一个栈数据结构:

class Queue<T> {
  private items: T[];
​
  constructor() {
    this.items = [];
  }
​
  push(item: T) {
    this.items.push(item);
  }
​
  pop(): T|undefined  {
    return this.items.shift();
  }
​
  size(): number {
    return this.items.length;
  }
}
​
let stringQueue= new Queue<string>();
stringQueue.push("TS");
stringQueue.push("JS");
console.log(stringQueue.pop()); // "TS"
console.log(stringQueue.pop()); // "TS"
console.log(stringQueue.pop()); // undefined 
console.log(stringQueue.size()); // 0let numberQueue = new Queue<number>();
numberQueue.push(1);
numberQueue.push(2);
console.log(numberQueue.pop()); // 1
console.log(numberQueue.size()); // 1

在这个例子中,我们定义了一个泛型类 Queue<T>,它具有三个方法:pushpopsize。在构造函数中,我们初始化了一个 items 数组,用于存储队列中的元素。

我们可以使用 stringQueuenumberQueue 两个实例来分别创建一个字符串队列和一个数字队列。通过调用 pushpop 方法,我们可以将元素写入队列中并从队列中弹出元素。

需要注意的是,使用泛型类时需要为泛型类型参数 T 指定具体的类型。在上面的示例中,我们为 stringQueuenumberQueue 分别指定了 stringnumber 类型。

总之,泛型类是 TypeScript 中非常强大和通用的特性。它可以帮助我们编写出更加灵活和可复用的代码,特别是在编写数据结构和算法等通用性较强的代码时,使用泛型类可以大大提高我们的编码效率和代码质量。

4 在TypeScript 中使用泛型约束

泛型约束是 TypeScript 中非常重要的一个概念,它可以帮助我们更好地控制泛型类型的范围和行为。在 TypeScript 中,我们可以使用 extends 关键字来约束泛型类型的范围,例如:

interface User {
  userName: string;
  age: number;
}
​
function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}
​
let user: User = { userName: "xiaohuo", age: 18 };
console.log(getProperty(user, "userName")); // 输出 "xiaohuo"
console.log(getProperty(user, "age")); // 输出 18
console.log(getProperty(user, "gender")); // 报错,因为 "gender" 不是 User 类型的属性

在上面的代码中,我们定义了一个 User 接口,它有 userNameage 两个属性。然后,我们定义了一个泛型函数 getProperty,它有两个泛型参数 TK。其中,泛型参数 T 是对象类型,泛型参数 KT 类型的属性名类型。在函数体中,我们使用 obj[key] 返回对象 obj 的属性值。由于泛型参数 K 受到了 keyof T 的约束,所以我们可以放心地使用 obj[key],而不必担心会访问到对象中不存在的属性。

总之,泛型约束是 TypeScript 中非常重要的一个概念,它可以让我们更好地控制泛型类型的范围和行为,提高代码的可读性和可维护性。