typescript快速上手
简介
参考文档
- https://ts.xcatliu.com/ 阮一峰typescript入门教程
- TypeScript: JavaScript With Syntax For Types. (typescriptlang.org) typescript官网
什么是TypeScript
TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
它的第一个版本发布于 2012 年 10 月,经历了多次更新后,现在已成为前端社区中不可忽视的力量,不仅在 Microsoft 内部得到广泛运用,而且 Google 开发的 Angular 从 2.0 开始就使用了 TypeScript 作为开发语言,Vue 3.0 也使用 TypeScript 进行了重构。
总结
- TypeScript 是添加了类型系统的 JavaScript,适用于任何规模的项目。
- TypeScript 是一门静态类型、弱类型的语言。
- TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性。
- TypeScript 可以编译为 JavaScript,然后运行在浏览器、Node.js 等任何能运行 JavaScript 的环境中。
- TypeScript 拥有很多编译选项,类型检查的严格程度由你决定。
- TypeScript 可以和 JavaScript 共存,这意味着 JavaScript 项目能够渐进式的迁移到 TypeScript。
- TypeScript 增强了编辑器(IDE)的功能,提供了代码补全、接口提示、跳转到定义、代码重构等能力。
- TypeScript 拥有活跃的社区,大多数常用的第三方库都提供了类型声明。
- TypeScript 与标准同步发展,符合最新的 ECMAScript 标准(stage 3)。
安装 TypeScript
| 1 | npm install -g typescript | 
以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。
编译一个 TypeScript 文件很简单:
| 1 | tsc hello.ts | 
我们约定使用 TypeScript 编写的文件以 .ts 为后缀,用 TypeScript 编写 React 时,以 .tsx 为后缀。
Hello TypeScript
首先编写 hello.ts
| 1 | function sayHello(person: string) { | 
然后执行编译命令
| 1 | tsc hello.ts | 
这时候会生成一个 hello.js,内容如下
| 1 | function sayHello(person) { | 
在 TypeScript 中,我们使用 : 指定变量的类型,: 的前后有没有空格都可以。
上述例子中,我们用 : 指定 person 参数类型为 string。但是编译为 js 之后,并没有什么检查的代码被插入进来。
这是因为 TypeScript 只会在编译时对类型进行静态检查,如果发现有错误,编译的时候就会报错。而在运行时,与普通的 JavaScript 文件一样,不会对类型进行检查。
现在我们来修改一下 hello.ts
| 1 | function sayHello(person: string) { | 
然后重新执行编译命令,发现报如下错误

但是 hello.js 文件仍然正常生成
| 1 | function sayHello(person) { | 
这是因为 ts 只会在编译时报错,但是在运行时不会提示错误
基础
原始数据类型
JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。
原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt。
布尔值(boolean)
布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean 定义布尔值类型
| 1 | let boolean: boolean = false | 
数值(number)
使用 number 定义数值类型
| 1 | let num1: number = 123 | 
字符串(string)
使用 string 定义字符串类型
| 1 | let str1: string = 'hello' | 
空值(void)
JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数
| 1 | function returnVoid(): void { | 
null 和 undefined
在 TypeScript 中,可以使用 null 和 undefined 来定义这两个原始数据类型
| 1 | let n: null = null | 
任意类型
声明方式
任意类型使用 any 关键字来表示。原始数据类型声明过类型后不能再赋值其他类型的数据,而任意值的类型可以赋值成多种数据类型
| 1 | let anystr: any = 'hello' | 
任意值的属性和方法
任意值属性可以访问任意方法和属性
| 1 | // 下面写法不会出错 | 
可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值
未声明类型变量
如果一个变量没有声明类型时,则默认也是任意值类型
| 1 | let something; | 
等同于
| 1 | let something: any; | 
类型推论
下面的代码虽然没有声明变量的类型,但是会报错
| 1 | let myFavoriteNumber = 'save' | 
这个代码等同于
| 1 | let myFavoriteNumber: string = 'save' | 
如果给变量声明了初始值,则ts会根据设置初始值类型推测出这个变量的数据类型,这就是类型推论
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:
| 1 | let notSetInitVal | 
联合类型
表示取值可以是多种类型中的一种
简单的举例
| 1 | let str4: number | string = 56 | 
多个类型使用 | 分隔,赋值时可以是声明了的类型,不能声明额外的数据类型
访问联合类型的属性或方法
当联合类型属性的值不确定时,只能访问联合类型中共用的属性或者方法,下面代码访问 num.toFixed(2)  出错是因为 string 类型没有这个方法
| 1 | // 访问联合类型的属性或方法 | 
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型
| 1 | let str6: string | number | 
上面的代码首先类型推断为字符串类型,所以可以使用 length 属性,然后又赋值为数字类型,所以可以使用 toFixed 方法
对象的类型-接口
什么是接口
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
简单的例子
| 1 | interface Person { | 
上面的例子中我们声明了一个接口 Person,并规定里面有两个属性,一个是 string 类型的 name,一个是 number 类型的 age。然后什么一个 str7 来实现这个接口,实现接口的对象的形状必须和接口一致,多一个属性或者少一个属性都不行
多一个时:

少一个时:

可见,赋值的时候,变量的形状必须和接口的形状保持一致
可选属性
有时我们希望不要完全匹配一个形状,那么可以用可选属性。
| 1 | interface Status { | 
在属性名称后面添加一个 ? 表示这是一个可选属性,实现这个接口时可以不声明这个属性,但是此时添加额外属性还是不行的
任意属性
有时候我们希望一个接口允许有任意的属性,可以使用如下方式
| 1 | interface Order { | 
注意:如果定义了任意属性,则其他属性的类型都必须是任意属性类型的子集。所以上面的接口中定义的任意属性的类型为 any
只读属性
如果希望一个属性只能在初始化时赋值,然后不能更改。可以使用 readonly 关键字定义只读属性
| 1 | interface ReadOnly { | 
数组的类型
在 typescript 中,数组类型有多中定义方法,比较灵活
类型+方括号表示法
声明了数组类型后,数组中不能有其他类型的数据,这是最常用的表达方式
| 1 | // number 类型的数组 | 
数组的一些方法参数也会收到类型影响
| 1 | // 类型“number”的参数不能赋给类型“string”的参数 | 
数组泛型
| 1 | let fibonacci: Array<number> = [1, 1, 2, 3, 5]; | 
泛型具体讲解在后面章节
用接口标识数组
| 1 | interface StrArr { | 
一般不这样做
类数组
类数组不是数组类型,比如 argument
| 1 | function sum() { | 
arguments 不是一个普通的数组,所以不能用普通的数组来表示,必须使用类数组
| 1 | function sum() { | 
事实上常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:
| 1 | function sum2() { | 
其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
| 1 | interface IArguments { | 
关于内置对象,查看后面的章节
any 在数组中的应用
any 表示数组中可以是任意类型的值
| 1 | let objarr:any[] = [ | 
函数类型
函数的声明
普通方式声明函数
| 1 | // 在typescript中当函数有输入和输出时,都要吧参数类型声明好 | 
函数表达式来声明函数
| 1 | // ts版本的函数表达式 | 
函数的参数一旦定义完成后,少传或者多传参数都是不允许的
| 1 | // 输入多余的参数或者少于参数都是不允许的 | 
用接口定义函数形状
| 1 | // 用接口定义函数形状 | 
可选参数
上面提到,参数声明好之后少传或者多传都是不允许的,但是实际情况中我们有的参数时有时无,这种时候我们可以使用可选参数
可选参数使用 ? 来表示
| 1 | const sum4 = (a: number, b: number, c?: number): number => { | 
这里需要注意的是,可选参数的位置必须放在最后面,否则报错
参数默认值
| 1 | // 参数默认值 | 
剩余参数
| 1 | // 剩余参数 | 
方法重载
| 1 | // 首先定义这个方法有那几种传参和返回的可能 | 
上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。
注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
类型断言
可以手动指定一个值的类型
语法:
| 1 | 值 as 类型 | 
将联合类型断言为其中一个类型
前面提到,在ts中不确定一个联合类型的变量是哪一个的时候,只能访问此联合类型中所有类型共有的属性或方法
| 1 | // 类型断言 | 
然而有时候,我们必须在不确定是那个类型的时候使用其中一个类型的属性,例如下面的代码
| 1 | // 类型断言 | 
这个时候就要使用断言
| 1 | // 类型断言 | 
将一个父类断言为一个具体的子类
| 1 | // 将一个父类断言为一个具体的子类 | 
函数isOK接收的是Error类型的参数,但是Error上没有code属性,所以将err参数断言为ApiError,就可以使用code属性进行条件判断
将任何一个类型断言为any
首先看下面的代码
| 1 | let num: number = 1 | 
当我们访问了一个类型上不存在属性或者方法时,ts会明确的给我们提示出错误原因,这一点是很有用的。
但是有时候我们必须要访问一个不存在的属性,例如:
| 1 | window.userId = 1001 | 
我们想在window上添加一个userId属性,这个时候ts会提示错误,我们可以使用 as any,来避免错误
| 1 | (window as any).userId = 1001 | 
注意的是这种语法会掩盖原有的错误提示,但是在开发中有时候这种写法反而可以提高开发效率
将any断言成一个具体的类型
| 1 | // 将any类型断言成一个具体的类型 | 
类型断言的限制
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为 any
- any 可以被断言为任何类型
- 要使得 A能够被断言为B,只需要A兼容B或B兼容A即可
声明文件
新语法索引
- declare var声明全局变量
- declare function声明全局方法
- declare class声明全局类
- declare enum声明全局枚举类型
- declare namespace声明(含有子属性的)全局对象
- interface和- type声明全局类型
- export导出变量
- export namespace导出(含有子属性的)对象
- export defaultES6 默认导出
- export =commonjs 导出模块
- export as namespaceUMD 库声明全局变量
- declare global扩展全局变量
- declare module扩展模块
- ///三斜线指令
什么是声明语句
例如我们在使用jQuery时,我们想要通过一个id获得元素,我们可以这样做
| 1 | $('#id') | 
但是在ts中,会提示:找不到名称“jQuery”,这时候需要使用 declare var 来声明
| 1 | declare var jQuery: (selector: string) => any; | 
上例中,declare var 并没有真的定义一个变量,只是定义了全局变量 jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。它编译结果是:
| 1 | jQuery('#foo'); | 
声明文件
通常我们会把声明专门放在一个文件中,这个文件就称为声明文件
例如新建  jQuery.d.ts,然后里面添加代码
| 1 | declare var jQuery: (selector: string) => any; | 
声明文件必须以 d.ts 结尾,typescript 会扫描所有以 .ts 结尾的文件,所以声明文件也会扫描到。然后再使用 jQuery 就不会出错了
当然,jQuery 的声明文件不需要我们定义了,社区已经帮我们定义好了:jQuery in DefinitelyTyped。
我们可以直接下载下来使用,但是更推荐的是使用 @types 统一管理第三方库的声明文件。
@types 的使用方式很简单,直接用 npm 安装对应的声明模块即可,以 jQuery 举例:
| 1 | npm install @types/jquery --save-dev | 
可以在这个页面搜索你需要的声明文件
书写声明文件
内置对象
JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。
内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。
ECMAScript 的内置对象
ECMAScript 标准提供的内置对象有:
| 1 | Boolean`、、、 等。`Error``Date``RegExp | 
我们可以在 TypeScript 中将变量定义为这些类型:
| 1 | let err: Error = new Error("this is a error") | 
DOM 和 BOM 的内置对象
DOM 和 BOM 提供的内置对象有:
| 1 | Document`、、、 等。`HTMLElement``Event``NodeList | 
TypeScript 中会经常用到这些类型:
| 1 | let body: HTMLElement = document.body; | 
它们的定义文件同样在 TypeScript 核心库的定义文件中。
用 TypeScript 写 Node.js
Node.js 不是内置对象的一部分,如果想用 TypeScript 写 Node.js,则需要引入第三方声明文件:
| 1 | npm install @types/node --save-dev | 
进阶
类型别名
使用关键词 type 来声明一个类型别名
| 1 | // 定义str表示string类型 | 
字符串字面量类型
| 1 | // 声明一个类型depts,值只能是下面定义的三个部门 | 
元祖
数组是吧相同类型的数据放在一个中括号中,而元祖是吧不同类型的元素放在一个中括号中
初始化赋值
| 1 | // 元祖 | 
直接对元祖赋值的时候。需要提供元祖中的所有类型值
| 1 | //=> err:不能将类型“[string]”分配给类型“y”。源具有 1 个元素,但目标需要 2 个 | 
越界的元素
使用 push 方法可以往元祖中追加元素,当超出初始化的长度时,则超出的元素类型将是元祖中每个类型的联合类型
| 1 | //=> err:类型“boolean”的参数不能赋给类型“string | number”的参数 | 
TS中的!和?用法
- 属性或参数中使用 ?:表示该属性或参数为可选项
- 属性或参数中使用 !:表示强制解析(告诉typescript编译器,这里一定有值),常用于vue-decorator中的@Prop
- 变量后使用 !:表示类型推断排除null、undefined
枚举
枚举类型用于取值被限制在某一范围内的场景,比如一周只能有7天,颜色只能是红黄蓝
简单的例子
使用 enum 来定义枚举类型
| 1 | // 定义枚举类型 | 
声明的代码编译成JS后是这样的
| 1 | var weekDays; | 
手动赋值
| 1 | // 手动赋值枚举类型 | 
手动赋值也可以赋值小数或者负数
| 1 | enum weekDays3 { Sun = -2, Mon = 1.5, Tue, Wed, Thu, Fri, Sat } | 
常数项和计算所得项
我们上面定义的枚举值都是常数项,而经过计算才能得到值的项目被称为计算所得项。例如
| 1 | enum weekDays4 { Sun = -2, Mon = 1.5, Tue, Wed, Thu, Fri, Sat = 'sat'.length } | 
计算所得项后面也必须是计算所得项或者没有项
| 1 | enum weekDays4 { Sun = -2, Mon = 1.5, Tue, Wed, Thu, Fri, Sat = 'sat'.length, Test = 'Test'.length } | 
如何只定义常数枚举,可以使用关键字 const
| 1 | // 定义常数枚举 | 
JS中类的用法
类的概念
虽然 JavaScript 中有类的概念,但是可能大多数 JavaScript 程序员并不是非常熟悉类,这里对类相关的概念做一个简单的介绍。
- 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
- 对象(Object):类的实例,通过 new生成
- 面向对象(OOP)的三大特性:封装、继承、多态
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat和Dog都继承自Animal,但是分别实现了自己的eat方法。此时针对某一个实例,我们无需了解它是Cat还是Dog,就可以直接调用eat方法,程序会自动判断出来应该如何执行eat
- 存取器(getter & setter):用以改变属性的读取和赋值行为
- 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public表示公有属性或方法
- 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
es6中类的用法
属性和方法
| 1 | class Animal { | 
类的继承
使用关键字 extend
| 1 | // 类的继承 | 
存取器
在类中通过 get 和 set 方法来对某个属性进行赋值和读取操作
| 1 | class Animal { | 
由于类的概念是在 es6 之后才提出的,但是默认编译 ts 为 js 时会编译成 es5 的代码,所以在编译时可以指定编译后的js版本
指定 -t es6 即可将ts变为es6的js代码
| 1 | tsc .\jsClass.ts -t es6 | 
静态方法
方法前面声明关键字 static 表示这个方法为一个静态方法,不需要通过实例化对象来调用,直接通过 类名.方法名() 调用
| 1 | class Animal { | 
es7中类的用法
实例属性
es6中类里面的属性只能通过构造函数里面的 this.xxx = xxx 来定义,在es7中可以直接在类里面定义属性
| 1 | class Person{ | 
静态属性
顾名思义,可以直接通过类来访问某个属性
| 1 | class Person{ | 
TS中类的用法
public private 和 protected
- public 表示该属性或者方法是公共的,在类的外部也是可以被访问的,默认类中所有的方法和属性都是 public
- private 表示该属性或者方法是私有的,只允许在类内部使用
- protected 修饰的属性或方法是受保护的,它和 private类似,区别是它在子类中也是允许被访问的
通过代码来区别三者不同
| 1 | class PublicClass { | 
上面的代码将name属性设置成了public,所以可以在类的外部访问和赋值
| 1 | class PrivateClass { | 
上面的代码将name设置成了private,然后实例化一个PrivateClass类对象,通过对象来访问name时,会出现错误提示,提示:属性“name”为私有属性,只能在类“PrivateClass”中访问
| 1 | class ProtectedClass { | 
设置了 protected 修饰的属性,效果和 private 类似,但是 protected 修饰的属性或方法允许在子类中使用
readOnly
readOnly表示只读属性,无法修改这个属性的值
| 1 | class ReadOnlyClass { | 
注意:如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。
| 1 | class Animal { | 
抽象类
abstract 用于定义抽象类的抽象方法,抽象类不允许被实例化,抽象方法必须被子类实现
| 1 | abstract class AbstractClass{ | 
当实现一个抽象类时会出现错误提示
除此之外,抽象方法必须被子类实现,代码如下
当没有实现抽象方法时会出现下面的错误提示
| 1 | abstract class AbstractClass{ | 
必须实现父类的抽象方法
| 1 | abstract class AbstractClass { | 
类的类型
给类中的属性和方法添加类型限制很简单
| 1 | class ClassType { | 
类与接口
类实现接口
一般来讲,一个类可以继承另外一个类,多个类之间会存在相同的东西,我们可以吧这些相同的功能抽取成一个接口,让类去实现它。
举个例子:门的一个父类,防盗门是门的一个子类,现在我要给防盗门添加一个报警器,我可以实现一个报警器的接口,让防盗门去实现它,现在我又有一个车,车上面也要有报警器功能,我直接让车去实现报警器接口即可
代码实现
| 1 | // 报警器接口 | 
一个类可以实现多个接口
例如:现在除了实现报警器功能,再实现一个车灯的功能
| 1 | // 报警器接口 | 
接口继承接口
车灯接口继承报警器接口后,类去实现车灯接口时,也还是要实现三个方法
| 1 | // 报警器接口 | 
泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
简单的例子
| 1 | function createArray(total: number, value: any): Array<any> { | 
这段代码中,valarr 数组中的每一项都是任意类型,现在我们想让数组中的元素类型是我们输入的数据类型,这时候泛型就派上用场了
| 1 | // 添加泛型约束 | 
也可以不手动指定泛型,让类型推断自动推断出来数据类型

多个类型参数
| 1 | function swap<T, U>(tuple: [T, U]): [U, T] { | 
上面的代码会自动推断为:function swap<*number*, *string*>(tuple: [number, string]): [string, number]
泛型约束
在函数内部使用泛型变量的时候,由于事先不知道属性的具体类型,所以读取一些属性的时候会报错
| 1 | function getLength<T>(val:T){ | 
可以通过让泛型继承某个接口
| 1 | interface witchLenth { | 
多个类型互相约束
| 1 | // 多个类型之间互相约束 | 
泛型接口
使用泛型接口来约束函数形状
| 1 | // 我们可以使用泛型接口来定义函数形状 | 
可以吧泛型提前到接口上
| 1 | interface CreateArrFun3<T> { | 
泛型类
| 1 | class GenerClass<T>{ | 
泛型参数的默认类型
| 1 | // 泛型的默认类型 | 
声明合并
如果声明了同名的函数、接口、类,那么他们会合并成一个类型
函数的合并
我们可以使用重载,定义多个函数类型
| 1 | function reverse(val: number): number | 
接口合并
| 1 | interface Alarm { | 
这里接口合并时,当属性出现重复时,必须保证重名的属性类型一致,否则会报错
| 1 | interface Alarm { | 
工程
在 TypeScript 中使用 ESLint
安装Eslint
将下面的依赖安装在当前项目中
| 1 | npm install --save-dev eslint | 
由于 ESLint 默认使用 Espree 进行语法解析,无法识别 TypeScript 的一些语法,故我们需要安装 @typescript-eslint/parser,替代掉默认的解析器,别忘了同时安装 typescript:
| 1 | npm install --save-dev typescript @typescript-eslint/parser | 
接下来需要安装对应的插件 @typescript-eslint/eslint-plugin 它作为 eslint 默认规则的补充,提供了一些额外的适用于 ts 语法的规则。
| 1 | npm install --save-dev @typescript-eslint/eslint-plugin | 
创建配置文件
ESLint 需要一个配置文件来决定对哪些规则进行检查,配置文件的名称一般是 .eslintrc.js 或 .eslintrc.json。
当运行 ESLint 的时候检查一个文件的时候,它会首先尝试读取该文件的目录下的配置文件,然后再一级一级往上查找,将所找到的配置合并起来,作为当前被检查文件的配置。
我们在项目的根目录下创建一个 .eslintrc.js,内容如下:
| 1 | module.exports = { | 
检查一个ts文件
创建完配置文件后,来创建一个ts文件测试eslint是否能检查它,新建一个 index.ts 并输入如下内容
| 1 | var myName = 'Tom'; | 
然后再终端运行 ./node_modules/.bin/eslint index.ts,会出现如下错误

需要注意的是,我们使用的是 ./node_modules/.bin/eslint,而不是全局的 eslint 脚本,这是因为代码检查是项目的重要组成部分,所以我们一般会将它安装在当前项目中。
可是每次执行这么长一段脚本颇有不便,我们可以通过在 package.json 中添加一个 script 来创建一个 npm script 来简化这个步骤:
| 1 | { | 
这时只需执行 npm run eslint 即可。
检查整个项目的ts文件
一般我们都会吧代码放在 src 目录下,我们希望检查 src 目录下的所有 ts 文件,所以需要将 package.json 中的 eslint 脚本改为对一个目录进行检查。由于 eslint 默认不会检查 .ts 后缀的文件,所以需要加上参数 --ext .ts:
| 1 | { | 
此时执行 npm run eslint 即会检查 src 目录下的所有 .ts 后缀的文件。
在 VSCode 中集成 ESLint 检查
在编辑器中集成 ESLint 检查,可以在开发过程中就发现错误,甚至可以在保存时自动修复错误,极大的增加了开发效率。
要在 VSCode 中集成 ESLint 检查,我们需要先安装 ESLint 插件,点击「扩展」按钮,搜索 ESLint,然后安装即可。
VSCode 中的 ESLint 插件默认是不会检查 .ts 后缀的,需要在「文件 => 首选项 => 设置 => 工作区」中(也可以在项目根目录下创建一个配置文件 .vscode/settings.json),添加以下配置:
| 1 | { | 
这时再打开一个 .ts 文件,将鼠标移到红色提示处,即可看到这样的报错信息了

我们还可以开启保存时自动修复的功能,通过配置:
| 1 | { | 
就可以在保存文件后,自动修复为:
| 1 | let myName = 'Tom'; | 
使用 Prettier 修复格式错误
ESLint 包含了一些代码格式的检查,比如空格、分号等。但前端社区中有一个更先进的工具可以用来格式化代码,那就是 Prettier。
Prettier 聚焦于代码的格式化,通过语法分析,重新整理代码的格式,让所有人的代码都保持同样的风格。
首先需要安装 Prettier:
| 1 | npm install --save-dev prettier | 
然后创建一个 prettier.config.js 文件,里面包含 Prettier 的配置项。Prettier 的配置项很少,这里我推荐大家一个配置规则,作为参考:
| 1 | // prettier.config.js or .prettierrc.js | 
使用 AlloyTeam 的 ESLint 配置
ESLint 原生的规则和 @typescript-eslint/eslint-plugin 的规则太多了,而且原生的规则有一些在 TypeScript 中支持的不好,需要禁用掉。
这里我推荐使用 AlloyTeam ESLint 规则中的 TypeScript 版本,它已经为我们提供了一套完善的配置规则,并且与 Prettier 是完全兼容的(eslint-config-alloy 不包含任何代码格式的规则,代码格式的问题交给更专业的 Prettier 去处理)。
安装:
| 1 | npm install --save-dev eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-alloy | 
在你的项目根目录下创建 .eslintrc.js,并将以下内容复制到文件中即可:
| 1 | module.exports = { | 
更多的使用方法,请参考 AlloyTeam ESLint 规则
使用 ESLint 检查 tsx 文件
安装 eslint-plugin-react
| 1 | npm install --save-dev eslint-plugin-react | 
package.json 中的 scripts.eslint 添加 .tsx 后缀
| 1 | { | 
VSCode 的配置中新增 typescriptreact 检查
| 1 | { | 




