一、TypeScript类型补充
函数的参数类型
函数是JavaScript非常重要的组成部分,TypeScript允许我们指定函数的参数和返回值的类型。
参数的类型注解 :声明函数时,可以在每个参数后添加类型注解,以声明函数接受的参数类型:
1 2 3
| function sum(num1: number, num2: number) { console.log(num1 + num2); }
|
函数的返回值类型
也可以添加返回值的类型注解,这个注解出现在函数列表的后面:
1 2 3
| function sum(num1: number, num2: number):number { return num1 + num2; }
|
和变量的类型注解一样,我们通常情况下不需要返回类型注解,因为TypeScript会根据 return 返回值推断函数的返回类型
某些第三方库处于方便理解,会明确指定返回类型,看个人喜好;
匿名函数的参数
匿名函数与函数声明会有一些不同:
当一个函数出现在TypeScript可以确定该函数会被如何调用的地方时; 该函数的参数会自动指定类型
1 2 3
| const names = ['aaa','bbb','ccc']
names.forEach(item => {console.log(item)})
|
对象类型
如果希望限定一个函数接受的参数是一个对象,我们可以使用对象类型
1 2 3 4
| function demo(point: { x: number; y: number }) { console.log(point.x); console.log(point.y); }
|
在对象我们可以添加属性,并且告知TypeScript该属性需要是什么类型;
属性之间可以使用 , 或者 ; 来分割,最后一个分隔符是可选的;
每个属性的类型部分也是可选的,如果不指定,那么就是any类型;
可选类型
对象类型也可以指定哪些属性是可选的,可以在属性的后面添加一个?
1 2 3 4 5 6
| function demo(point: { x: number; y: number; z?: number }) { console.log(point.x); console.log(point.y); } demo({ x: 10, y: 30 }); demo({ x: 10, y: 20, z: 30 });
|
联合类型
TypeScript的类型系统允许我们使用多种运算符,从现有类型中构建新类型。
第一种组合类型的方法:联合类型(Union Type)
- 联合类型是由两个或者多个其他类型组成的类型;
- 表示可以是这些类型中的任何一个值;
- 联合类型中的每一个类型被称之为联合成员(union’s members)
1 2 3 4 5 6
| function demo(id: number | string) { console.log(id); } export {}; demo(10) demo('abc')
|
联合类型的使用注意
传入给一个联合类型的值是非常简单的:只要保证是联合类型中的某一个类型的值即可
但是我们拿到这个值之后,我们应该如何使用它呢?因为它可能是任何一种类型。
比如我们拿到的值可能是number,我们就不能对其调用string上的一些方法;
怎么处理这样的问题呢?
- 我们需要使用缩小(narrow)联合(后续会专门讲解缩小相关的功能);
- TypeScript可以根据我们缩小的代码结构,推断出更加具体的类型;
1 2 3 4 5 6 7 8
| function demo(id: number | string) { if (typeof id === 'string') { console.log(id.length); } } export {}; demo(10); demo('abc');
|
可选类型补充
可选类型可以看做是类型 和 undefined 的联合类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function demo(msg?: string) { console.log(msg); }
demo();
function demo1(msg: string | undefined) { console.log(msg); }
demo1(undefined);
|
类型别名
前面,我们通过在类型注解中编写对象类型和联合类型,但是当我们想要多次在其他地方使用时,就要编写多次。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function demo(point: { x: number; y: number }) { console.log(point.x); console.log(point.y); } function demo1(point: { x: number; y: number }) { console.log(point.x); console.log(point.y); } function demo2(point: { x: number; y: number }) { console.log(point.x); console.log(point.y); }
|
我们可以给对象类型起一个别名:
1 2 3 4 5 6 7 8 9 10 11 12 13
| type pointID = { x: number; y: number; }; function dem4(point: pointID) { console.log(point.x); console.log(point.y); } type ID = number | string; function deno5(id: ID) { console.log(id); }
|
类型断言
有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)
比如我们通过 document.getElementById,TypeScript只知道该函数会返回 HTMLElement ,但并不知道它具体的类型:
1 2 3
| const el = document.getElementById('app');
const el1 = document.getElementById('img') as HTMLImageElement;
|
TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本,此规则可防止不可能的强制转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Person {} class Student { study() { console.log('我爱学习'); } } function foo(p: Person) { console.log(p);
(p as Student).study(); } let stu = new Student(); foo(stu);
let message = 'aaa'; let num: number = 1;
num = message as any as number; console.log(num);
|
非空类型断言
编写下面的代码时,在执行ts的编译阶段会报错:
1 2 3 4
| function print(message?: string) { console.log(message.length); } print('hello');
|
因为传入的message有可能是为undefined的,这个时候是不能执行方法的
但是,我们确定传入的参数是有值的,这个时候我们可以使用非空类型断言:
非空断言使用的是 ! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测
1 2 3 4
| function print(message?: string) { console.log(message!.length); } print('hello');
|
可选链
可选链事实上并不是TypeScript独有的特性,它是ES11(ES2020)中增加的特性:
- 可选链使用可选链操作符 ?.
- 它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行
- 虽然可选链操作是ECMAScript提出的特性,但是和TypeScript一起使用更版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| type Person = { name: string; friends?: { name: string; age?: number; }; }; const info: Person = { name: 'xxxx', friends: { name: 'yyyy', }, };
console.log(info.name); console.log(info.friends);
console.log(info.friends?.age);
|
??和!!的作用
!!操作符:
- 将一个其他类型转换成boolean类型
- 类似于Boolean(变量)的方式
1 2 3 4
| const message: string = 'hello';
const flag = !!message;
|
??运算符:
是ES11增加的新特性
空值合并操作符(??)是一个逻辑操作符,当操作符的左侧是 null 或者 undefined 时,返回其右侧操作数, 否则返回左侧操作数
1 2 3 4 5 6
| let msg: string | null = null;
const res = message
const res1 = message ?? '默认值'
|
字面量类型
除了前面我们所讲过的类型之外,也可以使用字面量类型(literal types):
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const message = 'hello';
let msg: 'hello' = 'hello'; export {};
let position: 'left' | 'right' = 'left'; position = 'right';
let num:123 = 123
|
字面量推理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const info = { url: 'http://asdasdadad', method: 'GET', }; function req(url: string, method: 'GET' | 'POST') {}
type Request = { url: string; method: 'GET'; }; const info1: Request = { url: 'http://asdasdadad', method: 'GET', }; req(info1.url, info1.method);
req(info1.url, info1.method as 'GET');
const info3 = { url: 'http://asdasdadad', method: 'GET', } as const; req(info3.url, info3.method); export {};
|
类型缩小
类型缩小的英文是 Type Narrowing
我们可以通过类似于 typeof padding === "number"
的判断语句,来改变TypeScript的执行路径
在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为缩小
而我们编写的 typeof padding === "number
可以称之为 类型保护(type guards)
常见的类型保护有如下几种:
typeof
平等缩小(比如===、!==)
instanceof
in
等等…
typeof
1 2 3 4 5 6 7 8 9
|
function print(p: number | string) { if (typeof p === 'string') { console.log(p.length); } }
|
平等缩小
1 2 3 4 5 6 7 8 9 10 11 12 13
| /平等缩小 function print(p: 'left' | 'right') { if (p === 'left') { console.log(p); } switch (p) { case 'left': console.log(p); break; } }
|
instanceof
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function printTime(time: string | Date) { if (time instanceof Date) { console.log(time.toUTCString()); } } class Student { study() {} } class Teacher { teacher() {} } function work(p: Student | Teacher) { if (p instanceof Student) { console.log(p.study()); } }
|
in
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| type Fish = { swimming: () => void; }; type Dog = { running: () => void; };
function walk(animal: Fish | Dog) { if ('swimming' in animal) { console.log(animal.swimming()); } } const fish: Fish = { swimming() {}, }; walk(fish);
|