一、TypeScript类型补充

函数的参数类型

函数是JavaScript非常重要的组成部分,TypeScript允许我们指定函数的参数和返回值的类型。

参数的类型注解 :声明函数时,可以在每个参数后添加类型注解,以声明函数接受的参数类型:

  1. function sum(num1: number, num2: number) {
  2. console.log(num1 + num2);
  3. }

函数的返回值类型

也可以添加返回值的类型注解,这个注解出现在函数列表的后面:

  1. function sum(num1: number, num2: number):number {
  2. return num1 + num2;
  3. }

和变量的类型注解一样,我们通常情况下不需要返回类型注解,因为TypeScript会根据 return 返回值推断函数的返回类型

某些第三方库处于方便理解,会明确指定返回类型,看个人喜好;

匿名函数的参数

匿名函数与函数声明会有一些不同:

当一个函数出现在TypeScript可以确定该函数会被如何调用的地方时; 该函数的参数会自动指定类型

  1. const names = ['aaa','bbb','ccc']
  2. //item会自动指定为string类型
  3. names.forEach(item => {console.log(item)})

对象类型

如果希望限定一个函数接受的参数是一个对象,我们可以使用对象类型

  1. function demo(point: { x: number; y: number }) {
  2. console.log(point.x);
  3. console.log(point.y);
  4. }

在对象我们可以添加属性,并且告知TypeScript该属性需要是什么类型;

属性之间可以使用 , 或者 ; 来分割,最后一个分隔符是可选的;

每个属性的类型部分也是可选的,如果不指定,那么就是any类型;

可选类型

对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个?

  1. function demo(point: { x: number; y: number; z?: number }) {
  2. console.log(point.x);
  3. console.log(point.y);
  4. }
  5. demo({ x: 10, y: 30 });
  6. demo({ x: 10, y: 20, z: 30 });

联合类型

TypeScript的类型系统允许我们使用多种运算符,从现有类型中构建新类型。

第一种组合类型的方法:联合类型(Union Type)

  • 联合类型是由两个或者多个其他类型组成的类型;
  • 表示可以是这些类型中的任何一个值;
  • 联合类型中的每一个类型被称之为联合成员(union’s members)
  1. function demo(id: number | string) {
  2. console.log(id);
  3. }
  4. export {};
  5. demo(10)
  6. demo('abc')

联合类型的使用注意

传入给一个联合类型的值是非常简单的:只要保证是联合类型中的某一个类型的值即可

但是我们拿到这个值之后,我们应该如何使用它呢?因为它可能是任何一种类型。

比如我们拿到的值可能是number,我们就不能对其调用string上的一些方法;

怎么处理这样的问题呢?

  • 我们需要使用缩小(narrow)联合(后续会专门讲解缩小相关的功能);
  • TypeScript可以根据我们缩小的代码结构,推断出更加具体的类型;
  1. function demo(id: number | string) {
  2. if (typeof id === 'string') {
  3. console.log(id.length);
  4. }
  5. }
  6. export {};
  7. demo(10);
  8. demo('abc');

可选类型补充

可选类型可以看做是类型 和 undefined 的联合类型:

  1. function demo(msg?: string) {
  2. console.log(msg);
  3. }
  4. //不填参数也可以,因为参数是可选的
  5. demo();
  6. //可选类型可以看做是 类型 和 undefined 的联合类型
  7. function demo1(msg: string | undefined) {
  8. console.log(msg);
  9. }
  10. //但是这里不填参数需要传递一个undefined
  11. demo1(undefined);
  12. //两种打印结果都是undefined

类型别名

前面,我们通过在类型注解中编写对象类型和联合类型,但是当我们想要多次在其他地方使用时,就要编写多次。

  1. function demo(point: { x: number; y: number }) {
  2. console.log(point.x);
  3. console.log(point.y);
  4. }
  5. function demo1(point: { x: number; y: number }) {
  6. console.log(point.x);
  7. console.log(point.y);
  8. }
  9. function demo2(point: { x: number; y: number }) {
  10. console.log(point.x);
  11. console.log(point.y);
  12. }
  13. //每次都写过于冗杂无意义

我们可以给对象类型起一个别名:

  1. //可以起个别名 用type
  2. type pointID = {
  3. x: number;
  4. y: number;
  5. };
  6. function dem4(point: pointID) {
  7. console.log(point.x);
  8. console.log(point.y);
  9. }
  10. type ID = number | string;
  11. function deno5(id: ID) {
  12. console.log(id);
  13. }

类型断言

有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)

比如我们通过 document.getElementById,TypeScript只知道该函数会返回 HTMLElement ,但并不知道它具体的类型:

  1. const el = document.getElementById('app');
  2. const el1 = document.getElementById('img') as HTMLImageElement;

TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本,此规则可防止不可能的强制转换:

  1. class Person {}
  2. class Student {
  3. study() {
  4. console.log('我爱学习');
  5. }
  6. }
  7. function foo(p: Person) {
  8. console.log(p);
  9. //类型person上不存在属性study
  10. //p.study()
  11. //类型断言
  12. (p as Student).study();
  13. }
  14. let stu = new Student();
  15. foo(stu);
  16. let message = 'aaa';
  17. let num: number = 1;
  18. //不允许
  19. // num = message as number
  20. //这样可以但不建议
  21. num = message as any as number;
  22. console.log(num);

非空类型断言

编写下面的代码时,在执行ts的编译阶段会报错:

  1. function print(message?: string) {
  2. console.log(message.length);
  3. }
  4. print('hello');

因为传入的message有可能是为undefined的,这个时候是不能执行方法的

但是,我们确定传入的参数是有值的,这个时候我们可以使用非空类型断言:

非空断言使用的是 ! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测

  1. function print(message?: string) {
  2. console.log(message!.length);
  3. }
  4. print('hello'); //5

可选链

可选链事实上并不是TypeScript独有的特性,它是ES11(ES2020)中增加的特性:

  • 可选链使用可选链操作符 ?.
  • 它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行
  • 虽然可选链操作是ECMAScript提出的特性,但是和TypeScript一起使用更版本
  1. type Person = {
  2. name: string;
  3. friends?: {
  4. name: string;
  5. age?: number;
  6. };
  7. };
  8. const info: Person = {
  9. name: 'xxxx',
  10. friends: {
  11. name: 'yyyy',
  12. },
  13. };
  14. console.log(info.name);
  15. console.log(info.friends);
  16. //Object is possibly 'undefined'.
  17. // console.log(info.friends.age);
  18. console.log(info.friends?.age); //undefined

??和!!的作用

!!操作符:

  • 将一个其他类型转换成boolean类型
  • 类似于Boolean(变量)的方式
  1. const message: string = 'hello';
  2. //!message 相当于取反也就是false
  3. //所以需要再次取反,也就是!!message 相当于 Boolean(message)
  4. const flag = !!message;

??运算符:

是ES11增加的新特性

空值合并操作符(??)是一个逻辑操作符,当操作符的左侧是 null 或者 undefined 时,返回其右侧操作数, 否则返回左侧操作数

  1. let msg: string | null = null;
  2. //null
  3. const res = message
  4. //默认值
  5. const res1 = message ?? '默认值'

字面量类型

除了前面我们所讲过的类型之外,也可以使用字面量类型(literal types):

  1. //const声明的默认就是字面量类型
  2. const message = 'hello';
  3. let msg: 'hello' = 'hello';
  4. export {};
  5. //字面量类型和联合类型一起使用才有意义
  6. let position: 'left' | 'right' = 'left';
  7. position = 'right';
  8. //注意字面类型必须等于赋值
  9. let num:123 = 123
  10. //报错 两者不同
  11. // num = 456

字面量推理

  1. const info = {
  2. url: 'http://asdasdadad',
  3. method: 'GET',
  4. };
  5. function req(url: string, method: 'GET' | 'POST') {}
  6. //这是因为info.method默认是string类型 而函数要求传递的是GET或者POST类型
  7. // req(info.url, info.method);
  8. //解决办法1
  9. type Request = {
  10. url: string;
  11. method: 'GET';
  12. };
  13. const info1: Request = {
  14. url: 'http://asdasdadad',
  15. method: 'GET',
  16. };
  17. req(info1.url, info1.method);
  18. //解决方法2
  19. req(info1.url, info1.method as 'GET');
  20. //解决方法3
  21. const info3 = {
  22. url: 'http://asdasdadad',
  23. method: 'GET',
  24. } as const;
  25. req(info3.url, info3.method);
  26. export {};

类型缩小

类型缩小的英文是 Type Narrowing

  • 我们可以通过类似于 typeof padding === "number"判断语句,来改变TypeScript的执行路径

  • 在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为缩小

  • 而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards)

  • 常见的类型保护有如下几种:

    • typeof

    • 平等缩小(比如===、!==)

    • instanceof

    • in

等等…

typeof

  1. //type
  2. //整个if语句是类型缩小
  3. //typeof p === 'string'是类型保护
  4. function print(p: number | string) {
  5. if (typeof p === 'string') {
  6. console.log(p.length);
  7. }
  8. }

平等缩小

  1. /平等缩小
  2. function print(p: 'left' | 'right') {
  3. if (p === 'left') {
  4. console.log(p);
  5. }
  6. //类似平等缩小
  7. switch (p) {
  8. case 'left':
  9. console.log(p);
  10. break;
  11. }
  12. }

instanceof

  1. //instanceof
  2. function printTime(time: string | Date) {
  3. if (time instanceof Date) {
  4. console.log(time.toUTCString());
  5. }
  6. }
  7. class Student {
  8. study() {}
  9. }
  10. class Teacher {
  11. teacher() {}
  12. }
  13. function work(p: Student | Teacher) {
  14. if (p instanceof Student) {
  15. console.log(p.study());
  16. }
  17. }

in

  1. //in
  2. type Fish = {
  3. swimming: () => void;
  4. };
  5. type Dog = {
  6. running: () => void;
  7. };
  8. function walk(animal: Fish | Dog) {
  9. if ('swimming' in animal) {
  10. console.log(animal.swimming());
  11. }
  12. }
  13. const fish: Fish = {
  14. swimming() {},
  15. };
  16. walk(fish);