认识泛型

软件工程的主要目的是构建不仅仅明确和一致的API,还要让你的代码具有很强的可重用性

比如我们可以通过函数来封装一些API,通过传入不同的函数参数,让函数帮助我们完成不同的操作

但是对于参数的类型是否也可以参数化呢?

什么是类型的参数化?

一个需求:封装一个函数,传入一个参数,并且返回这个参数

如果我们是TypeScript的思维方式,要考虑这个参数和返回值的类型需要一致

  1. function foo (arg:number) :number {
  2. return arg
  3. }
  4. function foo1 (arg:string) :number {
  5. //报错 类型不匹配
  6. return arg
  7. }

当然可以使用any类型

泛型实现类型参数化

虽然any是可以的,但是定义为any的时候,我们其实已经丢失了类型信息

比如我们传入的是一个number,那么我们希望返回的可不是any类型,而是number类型

所以,我们需要在函数中可以捕获到参数的类型是number,并且同时使用它来作为返回值的类型

我们需要在这里使用一种特性的变量 - 类型变量(type variable),它作用于类型,而不是值

  1. function foo<Type>(arg: Type) {
  2. return arg;
  3. }
  4. foo<number>(20)
  5. // 不指定则为字面量类型
  6. foo(30)

使用两种方式来调用它:

方式一:通过 <类型> 的方式将类型传递给函数

方式二:通过类型推到,自动推到出我们传入变量的类型

  • 会推导出它们是字面量类型的,因为字面量类型对于我们的函数也是适用的

可以传入多个类型

  1. function foo1<T, E, O>(n1: T, n2: E, n3: O) {}
  2. //类型为number string {}
  3. foo1(20, 'abx', {});

n平时在开发中我们可能会看到一些常用的名称:

  • T:Type的缩写,类型
  • K、V:key和value的缩写,键值对
  • E:Element的缩写,元素
  • O:Object的缩写,对象

泛型接口

  1. class Point<T> {
  2. num: T;
  3. age: T;
  4. constructor(num: T, age: T) {
  5. this.num = num;
  6. this.age = age;
  7. }
  8. }
  9. let p = new Point('sss', '25');
  10. let p2: Point<number> = new Point(2, 3);
  11. class Point1<T, E> {
  12. num: T;
  13. age: E;
  14. constructor(num: T, age: E) {
  15. this.num = num;
  16. this.age = age;
  17. }
  18. }
  19. let p1 = new Point1('sss', 25);

泛型类

  1. class Point<T> {
  2. num: T;
  3. age: T;
  4. constructor(num: T, age: T) {
  5. this.num = num;
  6. this.age = age;
  7. }
  8. }
  9. let p = new Point('sss', '25');
  10. let p2: Point<number> = new Point(2, 3);
  11. class Point1<T, E> {
  12. num: T;
  13. age: E;
  14. constructor(num: T, age: E) {
  15. this.num = num;
  16. this.age = age;
  17. }
  18. }
  19. let p1 = new Point1('sss', 25);

泛型接口

  1. interface IInfo<T> {
  2. num: T;
  3. arr: T[];
  4. handfunc: (arg: T) => void;
  5. }
  6. const foo4: IInfo<number> = {
  7. num: 2,
  8. arr: [2, 3, 4],
  9. handfunc: (arg) => {
  10. console.log(arg);
  11. },
  12. };

泛型的类型约束

有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中:

比如string和array都是有length的,或者某些对象也是会有length属性的

那么只要是拥有length的属性都可以作为我们的参数类型,那么应该如何操作呢

  1. interface ILength {
  2. length: number;
  3. }
  4. function getLength<T extends ILength>(arg: T) {
  5. return arguments.length;
  6. }
  7. getLength('llll')
  8. getLength([23])