什么是 TypeScript
首先,我对 TypeScript 的理解如下:TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
其次引用官网的定义:
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source.
翻译成中文即是:
TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译工具可以运行在任何服务器和任何系统上。TypeScript 是开源的。
为什么选择 TypeScript
回答这个问题前,先来看看下面这些 JavaScript 中的常见错误:
- Uncaught TypeError: Cannot read property
- TypeError: ‘undefined’ is not an object
- TypeError: null is not an object
- TypeError: Object doesn’t support property
- TypeError: ‘undefined’ is not a function
- TypeError: Cannot read property ‘length’
JavaScript 只会在 运行时
才去做数据类型检查,而 TypeScript 作为静态类型语言,其数据类型是在 编译期间
确定的,编写代码的时候要明确变量的数据类型。使用 TypeScript 后,这些低级错误将不再发生。
TypeScript 官网列举了一些优势,不过我更愿意自己总结一下:
TypeScript 增加了代码的可读性和可维护性
-
类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了
-
可以在编译阶段就发现大部分错误,这总比在运行时候出错好
-
增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等
TypeScript 非常包容
-
TypeScript 是 JavaScript 的超集,
.js
文件可以直接重命名为.ts
即可 -
即使不显式的定义类型,也能够自动做出类型推论
-
可以定义从简单到复杂的几乎一切类型
-
即使 TypeScript 编译报错,也可以生成 JavaScript 文件
-
兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取
TypeScript 拥有活跃的社区
-
大部分第三方库都有提供给 TypeScript 的类型定义文件
-
Google 开发的 Angular2 就是使用 TypeScript 编写的
-
TypeScript 拥抱了 ES6 规范,也支持部分 ESNext 草案的规范
兼容 JavaScript 的灵活性
TypeScript 虽然严谨,但没有丧失 JavaScript 的灵活性,TypeScript 非常包容:
- TypeScript 通过
tsconfig.json
来配置对类型的检查严格程度。 - 可以把
.js
文件直接重命名为.ts
。 - 可以通过将类型定义为
any
来实现兼容任意类型。这里先简单介绍下 any 类型,后续会详细讲解。比如一个 string 类型,在赋值过程中类型是不允许改变的:
let brand: string = 'imooc'
brand = 1 // Type '1' is not assignable to type 'string'.ts(2322)
// 如果是 any 类型,则允许被赋值为任意类型,这样就跟我们平时写 JavaScript 一样了:
let brand: any = 'imooc'
brand = 1
- 即使 TypeScript 编译错误,也可以生成 JavaScript 文件。
三大框架支持
我们学习一门新技术会关心它的生命力问题,如果这门技术在较短时间内就要被淘汰,那花费大量的时间学习也是不划算的。TypeScript 能够保持长久生命力的另一个原因,就是统治前端的三大框架对 TypeScript 的支持。
- Angular 是 TypeScript 最早的支持者,Angular 官方推荐使用 TypeScript 来创建应用。
- React 虽然经常与 Flow 一起使用,但是对 TypeScript 的支持也很友好。
- Vue3.0 正式版即将发布,由 TypeScript 编写。
TypeScript 的缺点
-
有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念
-
短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript 能够减少其维护成本
-
集成到构建流程需要一些工作量
-
可能和一些库结合的不是很完美
安装 TypeScript
环境准备:
- 安装
Node.js
环境(version: 8.14.0+) - 确保
npm
或者yarn
可用
到 nodejs官网 根据自己的操作系统下载对应 Node.js 版本,Node.js 自带 npm。安装后,在 终端
执行如下命令,检查是否安装成功:
~ node -v
v10.16.3
~ npm -v
6.9.0
TypeScript 安装,TypeScript 的命令行工具安装方法如下:
npm install -g typescript
以上命令会在全局环境下安装 tsc
命令,安装完成之后,我们就可以在任何地方执行 tsc
命令了。
编译一个 TypeScript 文件很简单:
tsc hello.ts
我们约定使用 TypeScript 编写的文件以 .ts
为后缀,用 TypeScript 编写 React 时,以 .tsx
为后缀。
编辑器
TypeScript 最大的优势便是增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等。
主流的编辑器都支持 TypeScript,这里我推荐使用 Visual Studio Code。它是一款开源,跨终端的轻量级编辑器,内置了 TypeScript 支持。另外它本身也是用 TypeScript 编写的。下载安装:https://code.visualstudio.com/
获取其他编辑器或 IDE 对 TypeScript 的支持:
工程化方案
实际项目工程中,可以采取另一种工程化方案:
1.在 ts-practice 目录下创建 src 目录
mkdir src && touch src/index.ts
接下来用 npm 进行项目初始化(初始化过程中的交互命令有兴趣可自行查阅相关资料,目前一路按“回车键”即可):
npm init
你会发现目录中多了一个 package.json
文件,它定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、作者、license等信息)。
将 package.json 中入口文件选项改为刚刚创建的 index.ts:
{
"main": "src/index.ts",
}
然后,使用 tsc 命令进行初始化:
tsc --init
这时候你会发现目录下又多了一个 tsconfig.json
文件,它指定了用来编译这个项目的根文件和编译选项。
- 不带任何输入文件的情况下调用 tsc 命令,编译器会从当前目录开始去查找 tsconfig.json 文件,逐级向上搜索父目录。
- 当命令行上指定了输入文件时,tsconfig.json 文件会被忽略。
在 package.json 文件中,加入 script 命令以及依赖关系:
{
"name": "ts-practice",
"version": "1.0.0",
"description": "",
"main": "src/index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"tsc": "tsc"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^13.1.1",
"typescript": "^3.7.4"
}
}
根据配置文件 package.json 中的配置选项,下载所需模块,也就是配置项目所需的运行和开发环境:
npm install
你会看到多了一个 node_modules
文件夹和一个 package-lock.json
文件,node_modules
文件夹是项目的所有依赖包,package-lock.json
文件将项目依赖包的版本锁定,避免依赖包大版本升级造成不兼容问题。
与介绍 tsc 命令时一样,将以下内容写入 index.ts 文件:
// src/index.ts
export enum TokenType {
ACCESS = 'accessToken',
REFRESH = 'refreshToken'
}
在项目根目录输入编译命令:
npm run tsc
这时候可以看到多了一个 lib
文件夹,里面的内容就是项目的编译结果了! ???
Hello TypeScript
我们从一个简单的例子开始。创建一个文件hello.ts,代码如下
function sayHello(person: string) {
return `Hello, ${person}`
}
let user = "jacob"
console.log(sayHello(user))
然后执行(在IDEA编辑器中可以直接安装编译的插件,会自动编译成js文件,然后执行js文件)
tsc hello.ts
这时候会生成一个编译好的文件 hello.js和hello.js.map
function sayHello(person) {
return "Hello, " + person;
}
var user = "jacob";
console.log(sayHello(user));
//# sourceMappingURL=hello.js.map
TypeScript 中,使用 :
指定变量的类型,:
的前后有没有空格都可以。
上述例子中,我们用 :
指定 person
参数类型为 string
。但是编译为 js 之后,并没有什么检查的代码被插入进来。
TypeScript 只会进行静态检查,如果发现有错误,编译的时候就会报错。
let
是 ES6 中的关键字,和var
类似,用于定义一个局部变量,可以参阅 let 和 const 命令。
下面尝试把这段代码编译一下:
function sayHello(person: string) {
return `Hello, ${person}`
}
let user = [2, 3, 4]
console.log(sayHello(user))
编辑器中会提示错误,编译的时候也会出错:
Error:(7, 22) TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
但是还是生成了 js 文件:
function sayHello(person) {
return "Hello, " + person;
}
var user = [2, 3, 4];
console.log(sayHello(user));
//# sourceMappingURL=hello.js.map
TypeScript 编译的时候即使报错了,还是会生成编译结果,我们仍然可以使用这个编译之后的文件。
如果要在报错的时候终止 js 文件的生成,可以在 tsconfig.json
中配置 noEmitOnError
即可。关于 tsconfig.json,请参阅官方手册(中文版)。