TypeScript笔记

热编译

文件结构

project
│  gulpfile.js
│  package-lock.json
│  package.json
│  tsconfig.json

├─dist
│      bundle.js
│      index.html

├─node_modules


└─src
      greet.ts
      index.html
      main.ts
        

npm下包

gulpfile.js

tsconfig.json

此时,在项目更目录使用gulp命令即可执行热编译。

基础类型

布尔值(boolean)

数字(number)

Typescript里所有的数字都是浮点数。支持十进制和十六进制的字面量,还支持ECMAScript 2015中引入的二进制和八进制字面量。

字符串(string)

支持单、双引号以及模板字符串

数组

元组

先定义再使用

枚举

enum类型。使用枚举类型为一组数值赋予名字。

默认情况下,元素编号从0开始,也可以设置为手动指定编号

枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为2,但是不确定它映射到Color里的哪个名字,我们可以查找相应的名字:

any

使用any定义不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查

void

void类型与any类型相反,表示没有任何类型。常见于函数没有返回值时,则其返回值类型是void

声明一个void类型的变量只能赋值undefinednull

null和undefined

nullundefined类型对应的值就是unllundefined

never

never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。

object

object表示非原始类型,也就是除numberstringbooleansymbolnullundefined之外的类型。

类型断言

使用类型断言能让编译器知道你已经对类型进行了必须的检查,它不会再对数据进行检查和解构。

类型断言有两种形式。 其一是“尖括号”语法:

另一个为as语法:

两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;

然而,当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。

接口

TypeScript的核心原则之一是对值所具有的结构进行类型检查

必选属性

使用interface定义一个接口,将这个接口作为类型传入函数,则对这个函数进行传参时需要有对应类型的值

可选属性

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

只读属性

定义接口时,在属性吗赢钱加readonly来指定只读属性。

对象赋值后就不能再改变了

TypeScript具有ReadonlyArray<T>类型,数组创建后就不能再改变了

上面代码的最后一行,可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:

readonly vs const

最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用const,若做为属性则使用readonly

类的实现

类的继承

类从基类中继承了属性和方法。 这里, Dog是一个 派生类,它派生自 Animal 基类,通过 extends关键字。 派生类通常被称作 子类,基类通常被称作 超类

类的属性私有化

TypeScript里,成员都默认为 public。即所有成员默认可见。

理解 private

当成员被标记成 private时,它就不能在声明它的类的外部访问。比如:

理解 protected

protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。

readonly修饰符

你可以使用 readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。

存取器

TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。

静态属性

当类被实例化的时候才会被初始化的属性。 创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。

在这个例子里,我们使用 static定义 origin,因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候,都要在 origin前面加上类名。 如同在实例属性上使用 this.前缀来访问属性一样,这里我们使用 Grid.来访问静态属性。

函数

函数类型

给函数添加类型检查

可选参数和默认参数

JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined。 在TypeScript里我们可以在参数名旁使用 ?实现可选参数的功能。 比如,我们想让last name是可选的:

可选参数必须跟在必须参数后面。 如果上例我们想让first name是可选的,那么就必须调整它们的位置,把first name放在后面。

剩余参数

必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用 arguments来访问所有传入的参数。

在TypeScript里,你可以把所有参数收集到一个变量里:

剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( ...)后面给定的名字,你可以在函数体内使用这个数组。

这个省略号也会在带有剩余参数的函数类型定义上使用到:

泛型

泛型arrow-up-right的定义主要有以下两种:

  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)

  2. 在程序编码中一些包含参数的arrow-up-right。其参数可以代表类或对象等等。(现在人们大多把这称作模板arrow-up-right

一句话概况就是:使用泛型来创建可重用且支持多种类型的数据组件。

定义一个泛型函数

泛型的使用方法有两种:

第一种:传入所有参数,包含类型参数:

第二种方法更普遍。利用了类型推论

类型推论: 即编译器会根据传入的参数自动地帮助我们确定T的类型

泛型类

将泛型定义在类中:

泛型约束

定义一个接口来描述约束条件,比如说泛型里需要处理带有.length属性的所有类型,只要传入有这个类型的属性,则不报错。

泛型里使用类类型

枚举

使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。

数字枚举

定义了一个数字枚举, Up使用初始化为 1。 其余的成员会从 1开始自动增长。 换句话说,Direction.Up的值为 1Down2Left3Right4

如果不对枚举成员使用初始化,则将会从0开始。

字符串枚举

在一个字符串枚举里,每个成员都需要使用字符串字面量

异构枚举

枚举可以混合字符串和数字成员,但是并不推荐这样做

类型兼容性

高级类型

交叉类型(Intersection Types)

联合类型(Union Types)

下面是一个联合类型的例子

联合类型表示一个值可以是几种类型之一,使用|分隔每个类型。

如果一个值是联合类型,那只能访问这个类型里的成员

类型保护与区分类型(Type Guards and Differentiation Types)

套用上面的一段代码,当我们想判断pet是否为Fish时,我们需要在判断语句里额外添加类型断言

用户自定义类型保护

要自定义一个类型保护,需要定义一个函数,返回一个类型谓词

类型谓词:parameterName is Type,其中parameterName必须是当前函数签名里的一个参数名。

typeof类型保护

typeof类型保护只有两种形式能被识别:typeof v === "typename"typeof v !== "typename"

可以为null的类型

默认情况下,类型检查器认为 nullundefined可以赋值给任何类型。nullundefined是所有其它类型的一个有效值。 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行

--strictNullChecks标记可以解决此错误:当你声明一个变量时,它不会自动地包含 nullundefined。 你可以使用联合类型明确的包含它们:

可选参数与可选属性

使用了 --strictNullChecks,可选参数和可选属性都会被自动地加上 | undefined:

类型别名

类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。

可辨识联合

声明联合接口:

首先我们声明了将要联合的接口。 每个接口都有 kind属性但有不同的字符串字面量类型。 kind属性称做 可辨识的特征标签。 其它的属性则特定于各个接口。 注意,目前各个接口间是没有联系的。 下面我们把它们联合到一起:

现在我们使用可辨识联合:

索引类型(Index types)

通过 索引类型查询索引访问操作符:

编译器会检查 name是否真的是 Person的一个属性。 本例还引入了几个新的类型操作符。 首先是 keyof T索引类型查询操作符。 对于任何类型 Tkeyof T的结果为 T上已知的公共属性名的联合。 例如:

迭代器与生成器

迭代器

for..of vs. for..in 语句

for..offor..in均可迭代一个列表;但是用于迭代的值却不同,for..in迭代的是对象的 的列表,而for..of则迭代对象的键对应的值。

下面的例子展示了两者之间的区别:

生成器

目标为 ES5 和 ES3

当生成目标为ES5或ES3,迭代器只允许在Array类型上使用。 在非数组值上使用 for..of语句会得到一个错误,就算这些非数组值已经实现了Symbol.iterator属性。

编译器会生成一个简单的for循环做为for..of循环,比如:

生成的代码为:

目标为 ECMAScript 2015 或更高

当目标为兼容ECMAScipt 2015的引擎时,编译器会生成相应引擎的for..of内置迭代器实现方式。

模块

模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。 相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import形式之一。

导出

导出声明

任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出。

导出语句

重新导出

我们经常会去扩展其它模块,并且只导出那个模块的部分内容。 重新导出功能并不会在当前模块导入那个模块或定义一个新的局部变量。

或者一个模块可以包裹多个模块,并把他们导出的内容联合在一起通过语法:export * from "module"

默认导出

每个模块都可以有一个default导出。 默认导出使用 default关键字标记;并且一个模块只能够有一个default导出。 需要使用一种特殊的导入形式来导入 default导出。

类和函数声明可以直接被标记为默认导出。 标记为默认导出的类和函数的名字是可以省略的。

导入

模块的导入操作与导出一样简单。 可以使用以下 import形式之一来导入其它模块中的导出内容。

导入一个模块中的某个导出内容

可以对导入内容重命名

将整个模块导入到一个变量,并通过它来访问模块的导出部分

具有副作用的导入模块

尽管不推荐这么做,一些模块会设置一些全局状态供其它模块使用。 这些模块可能没有任何的导出或用户根本就不关注它的导出。 使用下面的方法来导入这类模块:

export =import = require()

CommonJS和AMD的环境里都有一个exports变量,这个变量包含了一个模块的所有导出内容。

CommonJS和AMD的exports都可以被赋值为一个对象, 这种情况下其作用就类似于 es6 语法里的默认导出,即 export default语法了。虽然作用相似,但是 export default 语法并不能兼容CommonJS和AMD的exports

为了支持CommonJS和AMD的exports, TypeScript提供了export =语法。

export =语法定义一个模块的导出对象。 这里的对象一词指的是类,接口,命名空间,函数或枚举。

若使用export =导出一个模块,则必须使用TypeScript的特定语法import module = require("module")来导入此模块。

Last updated