类型别名
给一个类型起一个新名字
1 | type Name = string; |
使用 type 创建类型别名
类型别名常用于联合类型
交叉类型
1 | type TouchEvent=React.TouchEvent&React.MouseEvent |
将多种类型合并为一种类型,新的类型能访问到多个类型的所有属性
。
联合类型
1 | type Source=10|20|30; |
a 的值只能是 10,20,30
1 | const attr:number|string=20; |
将多种类型合并为一种类型,新的类型能访问到多个类型的共有属性
。
1 | interface Bird{ |
联合类型的类型保护
自定义类型保护
1 | let pet=getSmallPet() |
typeof 类型保护
1 | function isNumber(x: any): x is number { |
instanceof 类型保护
1 | function isFish(pet:Fish|Bird):pet is Fish{ |
不为 null 的类型断言!
1 | name!.indexOf('a') |
自定义属性类型保护
1 | interface Square { |
索引类型
keyof T
,返回 T 中所有属性的联合类型T[k]
,返回 T 中 K 元素的类型T extends U
,T 继承至 U
1 | let obj = { |
映射类型
1 | interface Obj{ |
条件类型
1 | type TypeName < T >= |
内置条件类型
1 | Exclude<T,U>//同上diff |
字符串字面量类型
用来约束取值只能是某几个字符串中的一个
1 | type EventNames = "click" | "scroll" | "mousemove"; |
注意,类型别名与字符串字面量类型都是使用type
进行定义
元组
数组合并了相同类型的对象,元组(Tuple)合并了不同类型的对象。
声明
1 | let jerry: [string, number] = ["jerry", 25]; |
赋值
1 | let jerry: [string, number]; |
直接对元组赋值,需要提供所有元组类型中指定的项。
访问
1 | jerry[0].slice(1); |
越界元素
当添加越界的元素时,它的类型被限制为元组中每个类型的联合类型
1 | let jerry: [string, number]; |
枚举
Enum
定义
1 | enum Days { |
枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射。
编译结果
1 | var Days; |
映射
1 | console.log(Days["Sun"] === 0); //true |
手动赋值枚举项,可以不是数字,但是需要使用让 tsc 无视类型检查,但是后面的枚举项也需要手动赋值,否则无法获取初始值
1 | enum Days { |
编译后
1 | var Days; |
手动赋值的枚举项也可以是小数或负数,后面项的递增步长是 1
1 | enum Days { |
常数项和计算所得项
前面的例子都是常数项
计算所得项例子:
1 | enum Color { |
上面的例子不会报错,但是如果后面还有未手动赋值的项,就会因无法获得初始值而报错。
1 | enum Color { |
当满足以下条件时,枚举成员被当作常数:
- 不具有初始化函数并且之前的枚举成员是常数,后面的枚举成员就是前面的值+1,第一个枚举成员默认初始值为 0;
- 枚举成员使用常数枚举表达式初始化。常数枚举表达式是 TypeScript 表达式的子集,可以在编译阶段求值,当一个表达式满足下面条件之一时,就是一个常数枚举表达式:
- 数字字面量
- 引用之前定义的常数枚举成员
- 带括号的常数枚举表达式,如果这个成员是在同一个枚举类型中定义的,可以使用非限定名来引用
+
,-
,~
一元运算符应用于常数枚举表达式+
,-
,*
,/
,%
,<<
,>>
,>>>
,&
,|
,^
二元运算符,常数枚举表达式作为其一个操作对象。若常数枚举表达式求值后为NaN
或Infinity
,则会在编译阶段报错。
所有其它情况的枚举成员被当作是需要计算得出的值。
常数枚举
const enum
定义的枚举类型
1 | const enum Directions { |
编译为:
1 | var directions = [ |
常数枚举与普通枚举的区别在编译阶段被删除,并且不能包含计算成员
假如包含了计算成员,则会在编译阶段报错
1 | const enum Colors { |
外部枚举
使用declare
声明枚举类型
1 | declare enum Directions { |
编译后
1 | var directions = [ |
外部枚举与声明语句一样,常出现在声明文件中。
同时使用declare
和const
1 | declare const enum Directions { |
编译为
1 | var directions = [ |
类
类的概念
- 类(Class):定义一件事物的抽象特定,包含属性和方法
- 对象(Object):类的实例,通过 new 生成
- 面向对象(OOP)的三大特点:封装、继承、多态
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。调用端不需要知道细节,只能通过对外接口来访问该对象,同时也保证外界无法任意更改对象内部的数据
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。
- 存取器(getter & setter):用以改变属性的读取和赋值行为
- 修饰器(Modifiers):修饰符是关键字,用以限定成员或类型的性质。比如 public 表示公有属性或方法
- 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。
ES6 中类的用法
属性和方法
1 | class Animal { |
使用class
定义类,使用constructor
定义构造函数。
使用new
实例化
类的继承
1 | class Cat extends Animal { |
存取器
1 | class Animal { |
使用 getter 和 setter 改变属性的赋值和读取行为
静态方法
使用static
修饰符修饰的方法称为静态方法,不需要实例化,而是直接通过类来调用:
1 | class Animal { |
ES7 中类的用法
实例属性
ES6 中实例的属性只能通过构造函数中的this.xxx
来定义,ES7 提案中可以在类中定义
1 | class Animal { |
静态属性
ES7 提案中,可以使用static
定义一个静态属性
1 | class Animal { |
TypeScript 中类的用法
访问修饰符
public
修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public
的private
修饰的属性或方法是私有的,不能在声明它的类的外部访问protected
修饰的属性或方法是受保护的,和private
类似,区别在于它的子类中也允许被访问
public
1 | class Animal { |
private
希望有的属性无法直接存取的
1 | class Animal { |
编译后
1 | var Animal = (function () { |
注意,TypeScript 编译之后的代码中,并没有限制 private 属性在外部的可访问性。
private
修饰的属性或方法,在子类中也是不允许访问的
1 | class Animal { |
当构造函数修饰为private
时,该类不允许被继承或实例化
1 | class Animal { |
修饰符还可以使用在构造函数参数中,等同于类中定义该属性,使代码更简洁
1 | class Animal { |
protected
如果使用protected
修饰属性,则允许在子类中访问
1 | class Animal { |
用protected
修饰构造函数时,该类只允许被继承
1 | class Animal { |
readonly
只读关键字属性,只允许出现在属性声明或索引签名中。
1 | class Animal { |
注意,如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面
1 | class Animal { |
抽象类
abstract
用于定义抽象类和其中的抽象方法
- 抽象类不允许被实例化
1 | abstract class Animal { |
- 抽象类中的抽象方法必须被子类实现
1 | abstract class Animal { |
继承抽象类,实现抽象方法
1 | abstract class Animal { |
编译结果
1 | var Animal = (function () { |
需要注意的是,即使是抽象方法,TypeScript 的编译结果中,仍然会存在这个类
类的类型
给类加上 TypeScript 的类型
1 | class Animal { |
在 TypeScript 使用泛型创建工厂函数时,需要引用构造函数的类类型
1 | function create<T>(c: {new(): T; }): T {//这边的new()不好理解 |
传入一个参数,此参数实例化后生成是 T 类型
1 | class BeeKeeper { |
createInstance 函数的参数是构造函数没有参数的 A 类的类型。
类与接口
类实现接口
不同类之间共有特性,把特性提取成接口,用implements
关键字实现。提高了面向对象的灵活性
1 | interface Alarm { |
一个类实现多个接口
1 | interface Alarm { |
接口继承接口
1 | interface Alarm { |
接口继承类
1 | class Point { |
混合类型
使用接口定义一个函数需要符合的形状
1 | interface SearchFunc { |
函数还可以有自己的属性和方法
1 | function getCounter(): Counter { |
泛型
定义函数、接口或类的时候,先不指定具体的类型,而在使用的时候再指定类型
例子-简单
创建一个指定长度的数组,同时将每一项填充默认值
1 | function createArray(length: number, value: any): Array<any> { |
返回值为数组泛型,并没有准确定义。实际上每一项的类型就是value
的类型
1 | function createArray<T>(length: number, value: T): Array<T> { |
函数名字后的 T 定义任意输入类型,后面的参数value:T
和输出Array<T>
中即可使用。
调用的时候可以在函数名称后面指定类型,也可以不指定,而让类型推论自动推算。
多个类型参数
一次定义多个泛型参数
1 | function swap<T, U>(tuple: [T, U]): [U, T] { |
交换元组
泛型约束
在函数内部使用泛型变量,由于不知道它是哪种类型,所以不能随意操作它的属性或方法。
1 | function loggingIdentity<T>(arg: T): T { |
可以对泛型进行约束,只允许传入那些包含length
属性的变量:
1 | interface Lengthwise { |
使用extends
约束泛型T
,必须符合接口Lengthwise
的形状,也就是必须包含length
属性
如果传入的参数不符合类型约束,编译阶段就会报错
1 | loggingIdentity(7); |
多个类型参数之间也能相互约束:
1 | function copyFields<T extends U, U>(target: T, source: U): T { |
使用了两个类型参数,其中要求 T 继承 U,这样就保证了 U 上不会出现 T 中不存在的字段。
泛型接口
可以使用接口的方式来定义一个函数需要符合的形状
1 | interface CreateArrayFunc { |
可以把泛型接口提前到接口名上:
1 | interface CreateArrayFunc<T>{ |
注意,此时在使用泛型接口的时候,需要定义泛型的类型。
泛型类
1 | class GenericNumber<T> { |
泛型参数的默认类型
TypeScript2.3 以后,可以为泛型中的类型参数指定默认类 U 型,当使用泛型时没有在代码中直接指定类型参数,从实际参数中也无法推测出时,默认类型就会起作用。
1 | function createArray<T = string>(length: number, value: T): Array<T> { |
声明合并
如果定义了两个相同名字的函数、接口或类,会合并成一个类型。
接口的声明合并
1.非函数成员
注意,合并属性的类型必须是唯一的
1 | interface A{ |
2.函数成员
函数重载顺序:接口内部安装书写顺序,正序(先写的在前),接口之间,先写的在后
1 | interface A{ |
如果函数参数是字符串字面量,该函数声明会提升到最前面
1 | interface A{ |
命名空间和其他类型的合并
就相当于在原数据上增加属性
1 | function Lib() { } |
函数的合并
重载定义多个函数类型
1 | function reverse(x: number): number; |
类的合并
与接口的合并规则一致
模块导入与导出
ES6 模块导出,commonJS 模块导入
1 |
|
编写声明文件
global 库
//global-lib.js
1 | function globalLib(options) { |
global-lib.d.ts
1 | declare function globalLib(options: globalLib.Options): void; |
使用
引入<script src='src/lib/global-lib.js'></script>
1 | globalLib({ x: 1 }); |
module 库
module-lib.js
1 | const version = "1.0.0"; |
module-lib.d.ts
1 | declare function moduleLib(options: Options): void; |
使用
1 | import moduleLib from "./module-lib"; |
umd 库
umd-lib.js
1 | (function (root, factory) { |
umd-lib.d.ts
1 | declare namespace umdLib { |
使用 1
1 | import umdLib from "./umd-lib.js"; |
使用 2
引入<script src='src/lib/umd-lib.js'></script>
需要 ts 配置"allowUmdGlobalAccess": true
1 | umdLib.doSomething(); |
为库增加属性
1 | import m from "moment"; |
为全局类库增加方法(不建议使用)
1 | declare global { |
ts 配置选项
默认配置:ts -- init
生成 ts 默认配置文件
文件选项
files :指定 ts 编译的文件,数组
"files": ["src/a.ts"]
include :指定 ts 编译的路径或文件
"include": ["src/a.ts","src/lib"]
支持通配符"src"
:包括 src 里面所有的文件"src/*"
:仅包括 src 一级目录的文件"src/*/*"
:仅包括 src 二级目录的文件
编译配置拆分
tsconfig.base.json
1 | { |
tsconfig.json
1 | { |
编译选项
1 | { |
babel 配置
1 | loader: "ts-loader", |
提高打包效率
可以另外安装插件类型检查
1 | npm i fork-ts-checker-webpack-plugin -D |