TypeScript 初入坑

本文主要介绍我入坑 TypeScript 以后总结的一些相关配置,包括:

  • TypeScript 安装、配置与调试
  • TSLint 配置
  • TypeScript 编写与发布 npm 包
  • TypeScript 与 webpack、babel
  • TypeScript 测试与覆盖率

1. TypeScript 基本配置

1.1. 安装 TypeScript

全局安装:

1
yarn global add typescript

不过我个人还是更加推荐在每个 TypeScript 项目下都安装一个开发依赖:

1
yarn add -D typescript

如果是在 Node.js 环境下编写,还推荐安装相应的类型声明:

1
yarn add -D @types/node

安装完后可以使用 tsc 对 TypeScript 文件进行编译:

1
2
3
4
# 全局安装
tsc test.ts
# 项目安装
npx tsc test.ts

使用 tsc --help 可以查看到编译选项。

1.2. 配置 tsconfig.json

tsc 提供的编译选项十分丰富,我们也经常需要在项目编译时指定一些参数,然后每次编译时都手动输入这些参数非常麻烦,我们可以在项目根目录创建 tsconfig.json 文件作为配置文件,运行 tsc 且不指定输入文件时,会默认应用文件中的配置。

一个简单的 tsconfig.json 如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"outDir": "./dist",
"noImplicitAny": true,
"module": "CommonJS",
"declaration": true,
"target": "ES2015",
"strict": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
  • outDir 指定了目标文件输出位置
  • noImplicitAny 检查在源代码中不能有隐式声明的 Any 类型
  • module 指定了模块规范
  • declaration 输出目标文件时同时输出类型声明文件
  • target 指定 JavaScript 的版本
  • strict 严格模式
  • includeexclude 指定输入和排除目录

详细配置可见 tsconfig.json

配置完后直接运行 tsc 即可。

1.3. 调试 TypeScript

这里演示如何在 VS Code 中调试 TypeScript。

调试的配置文件位于 .vscode/launch.json,建议将整个 .vscode 文件夹加入 .gitignore中。

调试编译后的 JavaScript

一种最简单的方法是调试编译后的 JavaScript:

.vscode/launch.json 中加入配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug JavaScript",
"program": "${workspaceRoot}/dist/index.js",
"args": [],
"protocol": "inspector",
"cwd": "${workspaceRoot}"
}
]
}

直接调试 TypeScript

直接调试 JavaScript 的好处是配置简单、但每次都需要显式地编译非常麻烦。这里使用 ts-node 作为运行环境,就可以直接调试 TypeScript 了。

安装 ts-node

1
yarn add -D ts-node

tsconfig.json 中开启 sourceMap

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"compilerOptions": {
"outDir": "./dist",
"noImplicitAny": true,
"module": "CommonJS",
"declaration": true,
"target": "ES2015",
"strict": true,
"sourceMap": true
},
"include": ["src"],
"exclude": ["node_modules"]
}

配置 .vscode/launch.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug TypeScript",
"args": ["${relativeFile}"],
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
"sourceMaps": true,
"protocol": "inspector",
"cwd": "${workspaceRoot}"
}
]
}

2. TSLint

2.1. 基本配置

编写 JavaScript 时一般会使用 ESLint 对代码进行检查。同样的, 在 TypeScript 中我们使用 TSLint。

首先需要安装依赖:

1
yarn add -D tslint

接着进行配置,在根目录创建 tslint.json 文件,一个典型的例子:

1
2
3
4
5
6
7
8
9
{
"extends": ["tslint:recommended"],
"rules": {
"quotemark": [true, "single"],
"no-empty": [true, "allow-empty-functions"],
"trailing-comma": [true, {"multiline": "always", "singleline": "never"}],
"interface-name": false
}
}
  • extends 预设的规则模板
  • rules 自定义规则

以上是我通常使用的规则,它在推荐规则的基础上,将字符串改为单引号、允许空函数、要求行尾逗号以及不要求接口名称必须以 I 开头。

完成安装和配置就可以对代码进行检查了:

1
npx tslint -p tsconfig.json

2.2. VS Code 配置

让 VS Code 支持 TSLint 错误显示和自动修复,需要在 VS Code 中安装 TSLint 插件。

在设置中将 TSLint 插件的 Auto Fix On Save 功能打开即可。

3. TypeScript 发布 npm 包

为了兼容 JavaScript 社区,发布到 npm 的包必须是 JavaScript 而不是 TypeScript。

所以发布一个包的步骤变为:

  1. 编译 TypeScript
  2. 让 npm 只识别编译后的 JavaScript
  3. 发布

3.1. 编译

在 npm scripts 中添加 build

1
2
3
"scripts": {
"build": "tsc"
}

3.2. 修改 npm files

package.json 中新加一个字段 files 指定 npm 识别的文件:

1
2
3
"files": [
"dist/**/*"
]

这样当 npm 发布包的时候,就只会放 files 字段中指定的文件打包发布。当然,npm 会自动识别 LICENSEREADME.mdpackage.json

按照如上配置,最后发布的包的目录结构为:

  • dist
    • index.js
    • ...
  • LICENSE
  • package.json
  • README.md

3.3. 自动化

谁都不想每次 npm publish 前都手动运行一次 yarn build ,因此你可以再添加一个 npm scripts:

1
2
3
4
"scripts": {
"build": "tsc",
"prepublishOnly": "yarn bulid"
}

这样每次 npm publish 前就会自动进行编译。

3.4. 避免丢失类型声明

为了兼容 JavaScript 社区,我们往 npm 发布了编译后的 JavaScript 代码,但编译成 JavaScript 后会失去类型声明。

我们可以在 tsconfig.json 中将 declaration 设为 true,使得编译时输出类型声明文件。

这样发布的 npm 包目录如下:

  • dist
    • index.js
    • index.d.ts
  • LICENSE
  • package.json
  • README.md

这样,JavaScript 和 TypeScript 都可以正常引用编译后的代码,且 TypeScript 还会自动引用同目录下的类型声明文件。(在 VS Code 下,JavaScript 也会自动引用类型声明文件作为代码提示。)

4. TypeScript 与 webpack

4.1. webpack 篇

如果使用 TypeScript 编写浏览器上运行的代码,一般需要配合 webpack 工作。

首先安装依赖:

1
yarn add -D webpack webpack-cli awesome-typescript-loader

在项目根目录下创建 webpack 配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const path = require('path');

function generateConfig(name) {
const mode = name.includes('min') ? 'production' : 'development';
return {
entry: './src/index.ts',
mode,
module: {
rules: [
{
test: /\.ts?$/,
loader: 'awesome-typescript-loader',
},
],
},
resolve: {
extensions: [ '.ts', '.js' ],
},
output: {
filename: `${name}.js`,
path: path.resolve(__dirname, 'dist'),
library: 'Test',
libraryTarget: 'umd',
},
devtool: 'source-map',
};
}

module.exports = [generateConfig('test'), generateConfig('test.min')];

这里的配置文件一次性生成了两个配置:

  • 在开发环境下编译 index.tstest.js,并生成 source map:在这种情况下,输出代码不会被压缩。
  • 在生产环境下编译 index.tstest.min.js,并生成 source map:在这种情况下,输出代码会被压缩。

注意:上述配置第 22 和 23 行定义了输出为 library,即只要运行编译后的代码,入口就会被自动挂载到浏览器的全局下。

例如 JQuery 可以这么配置:

1
2
3
4
5
6
7
8
...
output: {
filename: 'jquery.min.js',
path: path.resolve(__dirname, 'dist'),
library: '$',
libraryTarget: 'umd'
},
...

配置完成后,运行下面的命令进行编译:

1
npx webpack

4.2. Babel 篇

为了将新版本的 JavaScript 特性翻译成兼容性更高的 JavaScript,我们需要引入 Babel。

首先安装依赖:

1
yarn add -D @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/preset-typescript @babel/runtime babel-loader

修改 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const path = require('path');

function generateConfig(name) {
const mode = name.includes('min') ? 'production' : 'development';
return {
entry: './src/index.ts',
mode,
module: {
rules: [
{
test: /\.ts?$/,
loader: ['babel-loader', 'awesome-typescript-loader'],
},
],
},
resolve: {
extensions: [ '.ts', '.js' ],
},
output: {
filename: `${name}.js`,
path: path.resolve(__dirname, 'dist'),
library: 'Test',
libraryTarget: 'umd',
},
devtool: 'source-map',
};
}

module.exports = [generateConfig('test'), generateConfig('test.min')];

唯一的变化是 12 行,loader 中加入了 babel-loader

接下来在项目根目录新建 Babel 配置文件 .babelrc,典型配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"plugins": ["@babel/plugin-transform-runtime"],
"presets": [
"@babel/typescript",
["@babel/env", {
"modules": "umd",
"targets": {
"browsers": [ ">1%" ]
}
}]
]
}

编译后的版本将支持使用率大于 1% 的浏览器。

5. TypeScript 测试

5.1. 使用 Mocha 测试

这里使用的测试框架是 Mocha,断言库是简单优秀的的 power-assert。

安装依赖:

1
yarn add -D mocha @types/mocha power-assert espower-typescript

假定现在目录结构如下:

  • index.ts
  • test
    • index.test.ts

其中 index.ts

1
2
3
export function add(a: number, b: number): number {
return a + b;
}

index.test.ts

1
2
3
4
5
6
7
8
import assert = require('assert');
import { add } from '../index';

describe('Test add function', () => {
it('Add', () => {
assert(add(1, 2) === 3);
});
});

在 npm scripts 中添加:

1
2
3
"scripts": {
"test": "mocha --require espower-typescript/guess test/**/*.ts"
}

运行测试:

1
yarn test

5.2. 使用 istanbul 生成覆盖率报告

首先安装依赖:

1
yarn add -D istanbul@1.1.0-alpha.1 ts-node

注意这里必须安装 1.1.0-alpha.1 版本的 istanbul

在 npm scripts 中添加:

1
2
3
"scripts": {
"coverage": "istanbul cover -e .ts _mocha -- --require ts-node/register test/**/*.ts"
}

这里用来生成覆盖率的命令有些复杂,解析如下:

1
2
3
4
5
6
7
istanbul \						# istanbul 命令
cover \ # istanbul 子命令
-e .ts \ # istanbul 参数,指定 .ts 文件
_mocha \ # 在同一个进程内运行 mocha 保证 istanbul 能获得输出
-- \ # 两短横线后的参数会传递给 mocha 而非 istanbul
--require ts-node/register \ # 使用 ts-node 编译
test/**/*.ts # 测试文件

运行 yarn coverage 就会跑完测试代码并在 coverage 目录下生成覆盖率报告。建议将 coverage 添加到 .gitignore