TypeScript的高级类型简介

在前端开发中,大家入门ts其实是很简单的,只要掌握一些基本的类型知识就可以逐步的将js过渡到ts的应用,然而,当我们的项目很庞大复杂的时候,在开发过程中很容易将一些类型定义成any,这里主要介绍一下ts的高级类型,帮助大家更好的定义变量类型,从来减少any的使用。

泛型的介绍

泛型是强类型中比较重要的概念,使用泛型可以帮助我们提高代码的复用性,泛型是通过\color{DarkTurquoise}{<>}尖括号来表示的。尖括号中的字符被称作\color{DarkTurquoise}{类型变量},用来表示类型,接下来看一下概念中的描述:

泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型
示例:

 public testGenerics<T>(arg: T): T {   // T仅仅是个占位符,只不过大家都约定俗成的用T来占位
    return arg;
  }
  
  const str = this.testGenerics<string>('just test generics');

代码中的类型T,在没有调用testGenerics函数的时候是不确定的,只有在调用次方法之后,我们才能明确知道T代表的是什么类型,我们给他传入什么类型,他就给我们返回什么类型,这样数据就可以对应上了~

屏幕快照 2021-03-25 下午3.33.10.png

高级类型介绍

在实际开发中,大家可能比较常用一些基础类型,比如string、number、boolean等,但是当我们了解了一些高级类型后,我们可以定义更大复杂,更加灵活的接口类型。

1. 联合类型(|)

联合类型的规则和逻辑\color{DarkTurquoise}{“或”}是一致的,表示类型是连接多个类型中的任意一个

T | U

// demo
interface IPerson {
  age: number;
  gender: '女' | '男';
}

const person: Iperson = {
  age: 25;
  gender: '女'
}

2. 交叉类型(&)

交叉类型可以将多个类型合并成一个类型,写法和逻辑\color{DarkTurquoise}{“与”}相同

 T & U
 
 // demo 现在有两个类,我们可以通过交叉类型来实现一个新的属性的类型定义
 interface IPerson {
   age: number;
   gender: string;
 }

 interface IJob {
   title: string;
   years: number;
 }

 const life: IPerson & IJob = {
    age: 30,
    gender: 'nv',
    title: '开发',
    years: 10,
  };

3. 类型别名(type)

类型别名,它允许你为类型创建一个名字,这个名字就是类型的别名,从而你可以在多处使用这个别名,并且有必要的时候,你可以更改别名的值(类型),以达到一次替换,多处应用的效果,类型别名与声明变量的语法很类似,只需要把const,let换成type关键字即可。

type Alias = T | U
 
 // demo
 type sex =  '女' | '男';
 
 interface IPerson {
  age: number;
  gender: sex;
}

类型别名与接口的区别

  • 类型别名既可以表达接口所表达的类型,还比接口更加细粒度, 用起来更加随意
  • 接口interface可以被一个类class实现(implements),也可以使用extends来规范,但是类型别名是不行的
  • 类型别名可以与keyOf天然的结合使用

4. 类型索引(keyOf)

keyof 类似于 Object.keys ,用于获取一个接口中 Key 的联合类型

// demo
interface IPerson {
  age: number;
  gender: sex;
}

// 使用了keyOf后,只要IPerson修改了,type类型也会跟着自动修改

type personKeys = keyof IPerson;
//等价于
type personKeys = 'age' | 'gender';

5. 类型约束(extends

extends主要是用来对泛型加以约束的,他不像class使用extends是为了达到继承的目的。

 // demo
 type BaseType = string | number | boolean;
 
 public testGenerics<T extends BaseType>(arg: T): T {
   return arg;
 }
 
 this.testGenerics('123'); // 成功
 this.testGenerics({}); // 失败
屏幕快照 2021-03-25 下午4.48.16.png

extends的应用场景
extends 经常与 keyof 一起使用,例如我们有一个方法专门用来获取对象的值,但是这个对象并不确定,我们就可以使用 extends 和 keyof 进行约束,具体例子如下:

// 根据传入的obj来约束key的值
function getValue<T, K extends keyof T>(obj: T, key: K) {
  return obj[key]
}

6. 条件类型(U?X:Y)

条件类型的语法规则和三元表达式一致,一般用于一些类型不确定的情况

  T extends U ? X : Y // 如果T是U的子级 ,那么他的类型就是X,否则就是Y
  
  type Extract<T, U> = T extends U ? T : never; // 如果T是U的子级,那么返回T,否则抛弃

我们来看一下内置属性\color{DarkTurquoise}{Extract},他是用来提取公共属性的,接下来看一下实例:

 interface ITeacher {
  age: number;
  gender: sex;

}

interface IStudent {
  age: number;
  gender: sex;
  homeWork: string;
}

type CommonKeys = Extract<keyof ITeacher, keyof IStudent>; // "age" | "gender"
屏幕快照 2021-03-25 下午5.02.46.png

7. 类型映射(in)

in用来做类型映射,遍历已有接口的key或者遍历联合类型

type Test<T> = {
   [P in keyof T]: T[P];  
};
// keyof T  相当于 type ObjKeys = 'a' | 'b'
// P in ObjKeys 相当于执行了一次 forEach 的逻辑,遍历 'a' | 'b'

interface IObj {
  a: string;
  b: string;
}

type newObj = Test<IObj>;
屏幕快照 2021-03-25 下午5.07.40.png

工具泛型介绍

ts中还内置了许多工具泛型,接下来介绍一些常用的工具泛型

1. Partial

用于将一个接口的所有属性都设置成\color{DarkTurquoise}{可选状态},执行步骤就是先通过keyof T,取出类型变量T的所有属性,然后通过in进行循环,最后在每个属性上加上?

 type Partial<T> = {
    [P in keyof T]?: T[P]
 }
 
 // demo
 interface ITeacher {
  age: number;
  gender: sex;
}

const teacherA: Partial<ITeacher> = {age: 28};  // 此时用了Partial,即使不给gender赋值也不会报错

2. Required

Required的作用刚好与Partial相反,是将接口中的每个属性都改成\color{DarkTurquoise}{必须的},主要区别就是将Partial中的?替换成-?

type Required<T> = {
    [P in keyof T]-?: T[P]
}

3. Exclude

Exclude 的作用与上边介绍过的Extract刚好相反,如果 T 中的类型在 U 不存在,则返回,否则抛弃。

 type Exclude<T, U> = T extends U ? never : T
 
 //demo
 
 interface ITeacher {
  age: number;
  gender: sex;

}

interface IStudent {
  age: number;
  gender: sex;
  homeWork: string;
}

type ExcludeKeys = Exclude<keyof ITeacher, keyof IStudent>; // "homeWork"

4. Pick

Pick主要用于\color{DarkTurquoise}{提取接口中的某几个属性},我们可以通过这个泛型提取接口的属性,从而生成一个新的属性

type Pick<T, K extends keyof T> = {
    [P in K]: T[P]
}

// demo
interface IStudent {
  name: string
  age: number
  homework: string
}

type StudentA = Pick<IStudent, "name" | "age"> // 提取这个接口中的name和age属性

const zs: StudentA = {
  name: 'zs',
  age: 18
}

5. Omit

Omit 的作用刚好和 Pick 相反, 用来\color{DarkTurquoise}{排除接口中的某些属性}

type Omit<T, K extends keyof any> = Pick<
  T, Exclude<keyof T, K>
>

// demo
interface IStudent {
  name: string
  age: number
  homework: string
}

type StudentA = Omit<IStudent, "name"> // 排除掉这个接口中的name属性

const zs: StudentA = {
  age: 18,
  homework: '背诵课文'
}
屏幕快照 2021-03-25 下午5.37.43.png

总结

最近TypeScript发布了 4.0 的版本新增了很多功能,建议大家可以去更好的了解和掌握它,希望此篇文章可以帮助大家,让大家有所收获,减少甚至摆脱开发中的AnyScript~

推荐阅读更多精彩内容