TypeScript中的泛型

1. 泛型介绍

它的出现主要是为了解决参数与返回值类型之间的关系,比如一个函数接受一个类型的参数,并返回该参数,如果类型特别多就会很难写,可能会用到any,因为any可以代表任意类型的值,可也就相当于关闭了ts校验,此时泛型出现了,通过先定义一个占位(类型参数),等真正调用函数时,传入不同的类型,就代表函数的参数类型以及返回值类型。

泛型的特点就是带有类型参数。

如何定义?在函数名后使用尖括号<T>,尖括号里存放类型参数,可以为多个参数,等到调用时传入具体的类型即可,就代表该函数参数的类型是什么。这里的T是随便起的名字,只是一个占位,起什么名字都行,默认这个名字的首字母大写。

这里传入了number类型,正常情况下函数调用时就可以省略不写类型参数的值,ts会自动推断,当推断不出来时会报错,需要自己写入类型参数的值。

function getFirst<T>(arr:T[]):T {
  return arr[0];
}
getFirst<number>([1, 2, 3]);
getFirst([1, 2, 3]);

2. 写法

1. 函数的泛型写法

对于函数声明(也就是function声明的函数),尖括号放在函数名后面,尖括号里面存放类型参数。

function id<T>(arg:T):T {
  return arg;
}

对于变量定义的函数

写法一:在变量名后+ :+尖括号,尖括号里存放类型参数

写法二:使用对象形式:

// 写法一
let myId:<T>(arg:T) => T = id;

// 写法二
let myId:{ <T>(arg:T): T } = id;

2. 接口的泛型写法

写法一:直接在接口名后加尖括号。

interface Box<Type> {
  contents: Type;
}

let box:Box<string>;

写法二:定义在某个方法中

interface Fn {
  <Type>(arg:Type): Type;
}

function id<Type>(arg:Type): Type {
  return arg;
}

let myId:Fn = id;

写法一与写法二的不同点:写法一将类型参数之间写在外部,整个接口内部的属性和方法都能使用,而写法二是具体到某个方法上了,只能供自己使用。

3. 类的泛型写法

直接在类名后加尖括号即可,也可以用在类的表达式中,因为js中的类本质就是一个构造函数,所以泛型类也可以写成构造函数形式。

泛型类描述的是类的实例,而静态属性和静态方法只能定义在类的本身,所以这两者不能引用类型参数,否则会报错。

class Pair<K, V> {
  key: K;
  value: V;
}
type MyClass<T> = new (...args: any[]) => T;

4. 类型别名(type)的泛型写法

type也能使用泛型。

type Nullable<T> = T | undefined | null;

3. 类型参数的默认值

类型参数可以设置一个默认值,假如使用时,没有给出类型参数,ts会先自行推断,推断不出的使用默认值。

function getFirst<T = string>(
  arr:T[]
):T {
  return arr[0];
}

一旦类型参数有默认值,就表示它是可选参数。如果有多个类型参数,可选参数必须在必选参数之后。

<T = boolean, U> // 错误

<T, U = boolean> // 正确

4. 数组的泛型表示

使用ts原生的类型接口Array<T>

在ts中Array是一个泛型接口,基础实现如下

interface Array<Type> {

  length: number;

  pop(): Type|undefined;

  push(...items:Type[]): number;

  // ...
}

像ts中的Map、Set和Promise都是泛型接口,完整写法是Map<K, V>、Set<T>和Promise<T>

只读数组泛型:

ReadonlyArray<T>:数组成员是T类型,个别成员是这个类型

Readonly<T[]>:整个数组都是T[]类型,全部都是这个类型

5. 类型参数的约束条件

有时候对于传入的类型,需要在代码内进行判断是否满足某种情况,这时可以再设置类型参数时进行判断是否满足某种情况。

判断格式:

TypeParameter表示类型参数,extends是关键字,ConstraintType表示类型参数要满足的条件,就相当于类型参数是ConstraintType的子类即可。

<TypeParameter extends ConstraintType>

类型参数可以同时设置约束条件和默认值,但前提必须默认值满足约束条件

type Fn<A extends string, B extends string = 'world'>
  =  [A, B];

type Result = Fn<'hello'> // ["hello", "world"]

泛型本质上是一个类型函数,通过输入参数,获得结果,两者是一一对应关系。

约束条件不能约束自己。

6. 使用注意点

  • 尽量少用泛型

虽然泛型很灵活,但是会加大代码的复杂性,使其变得难读难写。

  • 类型参数越少越好

多一个类型参数,多一道替换步骤,加大复杂性。

  • 类型参数需要出现两次,如果在定义后只出现一次,不如不要
function greet<Str extends string>(
  s:Str
) {
  console.log('Hello, ' + s);
}

编译后,就只在定义参数时出现。

function greet(s:string) {
  console.log('Hello, ' + s);
}
  • 泛型可以嵌套,类型参数可以是另一个泛型
type OrNull<Type> = Type|null;

type OneOrMany<Type> = Type|Type[];

type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值