基础精粹
类型注意事项
数组类型
有两种类型注解方式,特别注意第二种使用 TS 内置的 Array 泛型接口。
let arr1: number[] = [1, 2, 3];
// 下面就是使用 TS 内置的 Array 泛型接口来实现的
let arr2: Array<number | string> = [1, 2, 3, "abc"];元组类型
元组是一种特殊的数组,限定了数组元素的个数和类型
let tuple: [number, string] = [0, "1"];需要注意元组的越界问题,虽然可以越界添加元素,但是仍然是不能越界访问,强烈不建议这么使用
tuple.push(2); // 不报错
console.log(tuple); // [0, "1", 2] 也能都打印出来
console.log(tuple[2]); // 但是想取出元组中的越界元素,就会报错元组长度是2,在index为2时没有元素函数类型
函数类型可以先定义再使用,具体实现时就可以不用注明参数和返回值类型了,而且参数名称也不用必须跟定义时相同。
对象类型
对象如果要赋值或者修改属性值,那么就不能用简单的对象类型,需要定义完整的对象类型
symbol 类型
symbol 类型可以直接声明为 symbol 类型,也可以直接赋值,跟 ES6 一样,两个分别声明的 symbol 是不相等的。
undefined 、null 类型
变量可以被声明为 undefined 和 null ,但是一旦被声明,就不能再赋值其他类型。
undefined 和 null 是任何类型的子类型,那就可以赋值给其他类型。但是需要设置配置项 "strictNullChecks": false
枚举类型
枚举分为数字枚举和字符串枚举,此外还有异构枚举(不推荐)
数字枚举
枚举既能通过名字取值,又能通过索引取值,我们具体看一下是怎么取到的。
字符串枚举
字符串枚举只能通过名字取值,不能通过索引取值。
常量枚举
用 const 声明的枚举就是常量枚举,会在编译阶段被移除。如下代码编译后 Month 是不产生代码的,只能在编译前使用,当我们不需要一个对象,但是需要一个对象的值的时候,就可以使用常量枚举,这样可以减少编译后的代码。
异构枚举
数字和字符串枚举混用,不推荐
枚举成员注意点
枚举成员是只读的,不能修改重新赋值
枚举成员的分为 const member 和 computer member
常量成员(const member),包括没有初始值的情况、对已有枚举成员的引用、常量表达式,会在编译的时候计算出结果,以常量的形式出现在运行时环境
计算成员(computer member),需要被计算的枚举成员,不会在编译阶段进行计算,会被保留到程序的执行阶段
在 computed member 后面的枚举成员,一定要赋一个初始值,否则报错
含字符串成员的枚举中不允许使用计算值(computer member),并且在字符串枚举成员后面的枚举成员必须赋一个初始值,否则会报错(见上面的异构类型)
数字枚举中,如果有两个成员有同样索引,那么后面索引会覆盖前面的(见下面的枚举 number )
枚举和枚举成员作为单独的类型
有以下三种情况,(1)枚举成员都没有初始值、(2)枚举成员都是数字枚举、(3)枚举成员都是字符串枚举
变量定义为数字枚举类型,赋值任意 number 类型的值都是可以的(可以超出枚举定义的数字范围),对枚举没有影响,但是不能赋值字符串等。
不同的枚举类型是不能比较的,不过同一个枚举类型是可以比较的,但是同一个枚举类型的不同枚举成员是不能比较的
变量定义为枚举类型,甚至就算定义为枚举类型的某个具体成员的类型,赋值也是对枚举没有影响的。(如下,E 和 F 的结果还是不变的)
字符串枚举类型的赋值,只能用枚举成员,不能随意赋值。(如果下 F)
接口类型
接口约束对象、函数、类的结构
对象类型接口
对象冗余字段
对象类型接口直接验证有冗余字段的对象字面量时会报错,这种冗余字段有时是不可避免的存在的。
解决方法一:在外面声明变量 result ,然后把 result 传入 render 函数,避免传入对象字面量。
解决方法二: 用类型断言(两种 as 和尖括号),但是如果对象字面中都没有符合的,还是会报错,可以用 as unknown as xxx
解决方法三:用字符串索引签名
接口属性可定义为只读属性和可选属性
可索引类型
不确定一个接口中有多少属性时,可以使用可索引类型。分为数字索引签名和字符串索引签名,如果接口定义了某一种索引签名的值的类型,之后再定义的属性的值必须是签名值的类型的子类型。可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。
函数类型接口
混合接口
混合接口,需要注意看一下,接口中的属性没有顺序之分,混合接口不需要第一个属性是匿名函数。
接口继承
函数类型相关
定义 TS 函数的四种方式,第一种方式可以直接调用,但是后三种就需要先实现定义的函数再调用
可选参数
可选参数必须位于必选参数之后,即可选参数后面不能再有必选参数
参数默认值
带默认值的参数不需要放在必选参数后面,但如果带默认值的参数出现在必选参数前面,必须明确的传入 undefined 值来获得默认值。在所有必选参数后面的带默认值的参数都是可选的,与可选参数一样,在调用函数的时候可以省略。
函数重载
要求定义一系列的函数声明,在类型最宽泛的版本中实现重载, TS 编译器的函数重载会去查询一个重载的列表,并且从最开始的一个进行匹配,如果匹配成功,就直接执行。所以我们要把大概率匹配的定义写在前面。
函数重载的声明只用于类型检查阶段,在编译后会被删除。
类
类属性和方法注意点
类属性都是实例属性,不是原型属性,而类方法都是原型方法
实例的属性必须具有初始值,或者在构造函数中初始化,除了类型为 any 的。
类的继承
派生类的构造函数必须包含“super”调用,并且访问派生类的构造函数中的 this 之前,必须调用“super"
类修饰符
1、public: 所有人可见(默认)。
2、 private: 私有属性
私有属性只能在声明的类中访问,在子类或者生成的实例中都不能访问,但是 private 属性可以在实例的方法中被访问到,因为也相当于在类中访问,但是子类的的实例方法肯定是访问不到的。
可以把类的 constructor 定义为私有类型,那么这个类既不能被实例化也不能被继承
3、 protected 受保护属性
受保护属性只能在声明的类及其子类中访问,但是 protected 属性可以在实例的方法中被访问到,因为也相当于在类中访问
可以把类的 constructor 定义为受保护类型,那么这个类不能被实例化,但是可以被继承,相当于基类
4、 readonly 只读属性
只读属性必须具有初始值,或者在构造函数中初始化,初始化后就不能更改了,并且已经设置过初始值的只读属性,也是可以在构造函数中被重新初始化的。但是在其子类的构造函数中不能被重新初始化。
5、 static 静态属性
只能通过类的名称调用,不能在实例和构造函数或者子类中的构造函数和实例中访问,但是静态属性是可以继承的,用子类的类名可以访问
注意:构造函数的参数也可以添加修饰符,这样可以将参数直接定义为类的属性
抽象类
只能被继承,不能被实例化的类。
在抽象类中可以添加共有的方法,也可以添加抽象方法,然后由子类具体实现
接口类
类实现接口时,必须实现接口的全部属性,不过类可以定义自己的属性
接口不能约束类的构造函数,只能约束公有成员
接口继承类
相当于把类的成员抽象出来,只有类的成员结构,但是没有具体实现
接口抽离类成员时不仅抽离了公有属性,还抽离了私有属性和受保护属性,所以非继承的子类都会报错
被抽象的类的子类,也可以实现类抽象出来的接口,而且不用实现这个子类的父类已有的属性
泛型
泛型函数
注意:用泛型定义函数类型时的位置不用,决定是否需要指定参数类型,见下面例子。
泛型函数例子
泛型接口
注意:泛型接口的泛型定义为全局时,实现必须指定一个参数类型,或者用带默认类型的泛型
泛型类
注意:泛型不能应用于类的静态成员。并且实例化时,不指定类型,就可以传入任何类型
泛型约束
约束泛型传入的类型
泛型总结
利用泛型,函数和类可以轻松地支持多种类型,增强程序的扩展性
不必写多条函数重载,冗长的联合类型声明,增强代码可读性
灵活控制类型之间的约束
类型检查机制
类型检查机制: TypeScript 编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为。其作用是辅助开发,提高开发效率
类型推断
类型推断: 指的是不需要指定变量的类型(函数的返回值类型),TypeScript 可以根据某些规则自动地为其推断出一个类型
基础类型推断
最佳通用类型推断
当需要从多个类型中推断出一个类型的时候,TypeScript 会尽可能的推断出一个兼容当前所有类型的通用类型
上下文类型推断
以上的推断都是从右向左,即根据表达式推断,上下文类型推断是从左向右,通常会发生在事件处理中。
类型断言
在确定自己比 TS 更准确的知道类型时,可以使用类型断言来绕过 TS 的检查,改造旧代码很有效,但是防止滥用。
类型兼容
当一个类型 Y 可以被赋值给另一个类型 X 时,我们就可以说类型 X 兼容类型 Y
X兼容Y:X(目标类型) = Y(源类型)
接口兼容
成员少的兼容成员多的
函数兼容性
1、参数个数
固定参数
目标函数的参数个数一定要多于源函数的参数个数
Handler 目标函数,传入 test 的 参数函数 就是源函数
可选参数和剩余参数
(1) 固定参数是可以兼容可选参数和剩余参数的
(2) 可选参数是不兼容固定参数和剩余参数的,但是可以通过设置"strictFunctionTypes": false 来消除报错,实现兼容
(3) 剩余参数可以兼容固定参数和可选参数
2、参数类型
基础类型
接口类型
接口成员多的兼容成员少的,也可以理解把接口展开,参数多的兼容参数少的。对于不兼容的,也可以通过设置"strictFunctionTypes": false 来消除报错,实现兼容
3、返回值类型
目标函数的返回值类型必须与源函数的返回值类型相同,或者是其子类型
4、函数重载
函数重载列表(目标函数)
函数的具体实现(源函数)
目标函数的参数要多于源函数的参数才能兼容
返回值类型不兼容
枚举类型兼容性
枚举类型和数字类型是完全兼容的
枚举类型之间是完全不兼容的
类的兼容性
和接口比较相似,只比较结构,需要注意,在比较两个类是否兼容时,静态成员和构造函数是不参与比较的,如果两个类具有相同的实例成员,那么他们的实例就相互兼容
私有属性
类中存在私有属性情况有两种,如果其中一个类有私有属性,另一个没有。没有的可以兼容有的,如果两个类都有,那两个类都不兼容。
如果一个类中有私有属性,另一个类继承了这个类,那么这两个类就是兼容的。
泛型兼容
泛型接口
泛型接口为空时,泛型指定不同的类型,也是兼容的。
如果泛型接口中有一个接口成员时,类型不同就不兼容了
泛型函数
两个泛型函数如果定义相同,没有指定类型参数的话也是相互兼容的
兼容性总结
结构之间兼容:成员少的兼容成员多的
函数之间兼容:参数多的兼容参数少的
类型保护机制
指的是 TypeScript 能够在特定的区块(类型保护区块)中保证变量属于某种特定的类型。可以在此区块中放心地引用此类型的属性,或者调用此类型的方法。
前置代码,之后的代码在此基础运行
实现 getLanguage 方法直接用 lang.helloJava 是不是存在作为判断是会报错的
利用之前的知识可以使用类型断言解决
类型保护第一种方法,instanceof
类型保护第二种方法, in 可以判断某个属性是不是属于某个对象
类型保护第三种方法, typeof 类型保护,可以帮助我们判断基本类型
类型保护第四种方法,通过创建一个类型保护函数来判断对象的类型
类型保护函数的返回值有点不同,用到了 is ,叫做类型谓词
总结
不同的判断方法有不同的使用场景:
typeof:判断一个变量的类型(多用于基本类型)
instanceof:判断一个实例是否属于某个类
in:判断一个属性是否属于某个对象
类型保护函数:某些判断可能不是一条语句能够搞定的,需要更多复杂的逻辑,适合封装到一个函数内
高级类型
交叉类型
用 & 符号。虽然叫交叉类型,但是是取的所有类型的并集。
联合类型
声明的类型并不确定,可以为多个类型中的一个,除了可以是 TS 中规定的类型外,还有字符串字面量联合类型、数字字面量联合类型
对象联合类型
对象的联合类型,只能取两者共有的属性,所以说对象联合类型只能访问所有类型的交集
可区分的联合类型
这种模式是结合了联合类型和字面量类型的类型保护方法,一个类型如果是多个类型的联合类型,并且每个类型之间有一个公共的属性,我们就可以凭借这个公共属性来创建不同的类型保护区块。
核心是利用两种或多种类型的共有属性,来创建不同的代码保护区块
下面的函数如果只有 Square 和 Rectangle 这两种联合类型,没有问题,但是一旦扩展增加 Circle 类型,类型校验就不会正常运行,而且也不报错,这个时候我们是希望代码有报错提醒的。
如果想要得到正确的报错提醒,第一种方法是设置明确的返回值,第二种方法是利用 never 类型.
第一种方法是设置明确的返回值
第二种方法是利用 never 类型,原理是在最后 default 判断分支写一个函数,设置参数是 never 类型,然后把最外面函数的参数传进去,正常情况下是不会执行到 default 分支的。
索引类型
索引类型的查询操作符
keyof T 表示类型 T 的所有公共属性的字面量的联合类型
索引访问操作符
T[K] 表示对象 T 的属性 K 所代表的类型
泛型约束
T extends U 泛型变量可以继承某个类型获得某些属性
先看如下代码片段存在的问题。
解决如下
映射类型
可以从一个旧的类型,生成一个新的类型
以下代码用到了 TS 内置的映射类型
条件类型
T extends U ? X : Y
分布式条件类型
(A | B) extends U ? X : Y 等价于 (A extends U ? X : Y) | (B extends U ? X : Y)
用法一:利用分布式条件类型可以实现 Diff 操作
用法二:在 Diff 的基础上实现过滤掉 null 和 undefined 的值。
以上的类型别名在 TS 的类库中都有内置的类型
Diff => Exclude<T, U>NotNull => NonNullable<T>
此外,内置的还有很多类型,比如从类型 T 中抽取出可以赋值给 U 的类型 Extract<T, U>
比如: 用于提取函数类型的返回值类型 ReturnType<T>
先写出 ReturnType<T> 的实现,infer 表示在 extends 条件语句中待推断的类型变量。
分析一下上面的代码,首先要求传入 ReturnType 的 T 必须能赋值给一个最宽泛的函数,之后判断 T 能不能赋值给一个可以接受任意参数的返回值待推断为 R 的函数,如果可以,返回待推断返回值 R ,如果不可以,返回 any 。
创建运维管理主文件夹,创建地域管理和主机集合的文件夹和路由访问文件
Last updated