TS中的泛型(generics)

2024-01-01 20:00:30

目录

什么是泛型

为什么要有泛型

示例代码

泛型函数

泛型类

泛型的继承

泛型去继承一个类

泛型去继承一个接口


什么是泛型

在 TypeScript 中,泛型(Generics)是一种允许在定义类、接口和函数时,延迟指定其中某些类型的概念。泛型可以使代码更具有通用性和灵活性,因为它可以用于处理多种类型的数据。

泛型通过使用泛型参数(Generic parameter)来实现。泛型参数使用尖括号(<>)括起来放在类型的名称后面。这个参数可以在类、接口或函数内部代表任何类型。

为什么要有泛型

泛型在编程中扮演着重要的角色,主要有以下几个原因:

1. 提高代码的灵活性和复用性:泛型允许我们编写通用的代码,能够适应多种数据类型。通过使用泛型,我们可以在类、接口和函数中定义使用参数化类型的逻辑,从而避免相同的代码重复编写。

2. 类型安全性:在使用泛型的情况下,编译器可以在编译时进行类型检查和推断。这意味着编译器可以确保数据的一致性以及正确的类型使用,减少程序在运行时发生类型错误的可能性。

3. 提高代码的可读性和可维护性:通过使用泛型,我们可以使代码更加清晰、易读和可维护。泛型参数可以提供有意义的类型名称,并且在代码中使用泛型的地方可以直观地表达意图。

4. 适应不同的数据类型:在处理不同类型的数据时,使用泛型可以确保代码的适应性和通用性。无论是处理字符串、数字、对象还是其他自定义类型,泛型可以在不修改代码的情况下适应这些类型的变化。

5. 避免类型断言和类型转换:使用泛型可以避免手动进行类型转换和类型断言的繁琐操作。泛型提供了类型推断和类型约束的功能,使得代码更加简洁和可维护。

总的来说,泛型使得我们可以编写更加灵活、可复用和类型安全的代码。它不仅提高了开发效率,还减少了错误和调试的成本。因此,在需要处理多种数据类型的情况下,使用泛型是非常有价值的。

示例代码

function save(a:number):number {
    return a;
}

let s = save(12)
// 现在这个函数功能比较单一,只能存数字,但是如果我们想可以让这个函数既能存数组又能存字符串,那么这个时候,我们首先想到的联合类型
function save(a:number|string):number|string {
    return a;
}
let ss:number|string = save(12)
let sss:number|string = save('stt')
//如果用联合类型,那么我现在还想存Date、Biolean、自定义的User类型。那么咱们这个联合写不下去了。
// 所以咱么就想到一个比较极端的方法,我让这个函数的参数是一个any类型。
function save(a:any):any {
    return a;
}
let b = save(12);
let c = save('str')
let d:number = save('ghirgh') // 也能过。any就相当于没有类型,一般不用any

正确写法,应该把类型当成参数传进去。也就是咱们一开始的时候,确定不了这个类型,但是用的时候,能够确定

// T只是一个占位符,可以用任何字母去表示
function save<T>(a:T):T {
  return a;
}

let s:string = save<number>(12) // 编译不过,因为在调用这个函数的时候,传递了一个number,但是s是string类型的。
let ss = save('str'); // 可以通过类型推测,推测出是一个string类型
let sss = save(true)

泛型函数

泛型函数其实就是在函数后面加上<T>,T可以用任何类型都代替

// 泛型函数
function joinArray<T>(...args:T[][]):T[] {
  let result:T[] = [];
  args.forEach(arr => {
    result = result.concat(arr)
  })
  return result;
}
// [1, 2, 3, 'a']
let arr = joinArray<number| string>([1, 2, 3], ['a', 'b', 'c'])
console.log(arr)

泛型类

class Box<T> {
  private value: T;

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

  getValue(): T {
    return this.value;
  }
}

const numberBox = new Box<number>(42);
console.log(numberBox.getValue()); // 输出: 42

const stringBox = new Box<string>('Hello');
console.log(stringBox.getValue()); // 输出: 'Hello'

泛型的继承

泛型去继承一个类
  • 当泛型去继承一个类的时候,并不是代表这个类具有了泛型继承的这个类的所有属性和方法。而是在用这个类的时候,接收的参数,只能是当前类及其子类
class Box<T> {
  value: T;

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

  getValue(): T {
    return this.value;
  }
}

class NumberBox<T extends number> extends Box<T> {
  add(other: NumberBox<T>): NumberBox<T> {
    const sum = this.value + other.getValue();
    return new NumberBox<T>(sum as T);
  }

  subtract(other: NumberBox<T>): NumberBox<T> {
    const diff = this.value - other.getValue();
    return new NumberBox<T>(diff as T);
  }
}

const number1 = new NumberBox<number>(42);
const number2 = new NumberBox<number>(10);

const result1 = number1.add(number2);
console.log(result1.getValue()); // 输出: 52

const result2 = number1.subtract(number2);
console.log(result2.getValue()); // 输出: 32

Box?类是一个泛型基类,NumberBox?类继承自?Box?类,并扩展了泛型参数的范围。NumberBox?类仅接受类型为?number?的泛型参数,因此在?add?和?subtract?方法中使用泛型参数?T?时,编译器可以确定?T?的实际类型,从而可以保证运算的正确性。

需要注意的是,当定义扩展泛型基类的类时,需要在类的名称后添加泛型参数的范围约束,以确保子类的类型参数满足基类的泛型参数约束。例如,在上述示例中,NumberBox?类继承自?Box<number>?类型,泛型参数的范围约束为?T extends number,表示子类的泛型参数必须是?number?类型或其子类型。

泛型去继承一个接口
interface Printable {
  print(): void;
}

function printItem<T extends Printable>(item: T): void {
  item.print();
}

class Book implements Printable {
  print(): void {
    console.log("Printing book...");
  }
}

class Magazine implements Printable {
  print(): void {
    console.log("Printing magazine...");
  }
}

printItem(new Book());     // 输出: Printing book...
printItem(new Magazine()); // 输出: Printing magazine...

我们定义了一个名为?Printable?的接口,该接口包含一个?print?方法。然后,我们使用泛型继承了?Printable?接口,并在?printItem?函数中使用泛型参数?T?扩展了?Printable?接口。这样,我们可以确保传递给?printItem?函数的参数满足?Printable?接口的要求。

在函数调用中,我们分别传递了?Book?和?Magazine?类的实例作为参数。由于?Book?和?Magazine?类都实现了?Printable?接口,且满足?T extends Printable?泛型约束,所以它们可以传递给?printItem?函数,并且能够调用?print?方法进行打印操作。

通过泛型继承接口,我们可以在函数中对泛型类型进行更加精确的约束,以确保类型的一致性和兼容性。

文章来源:https://blog.csdn.net/kkkys_kkk/article/details/135327346
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。