为什么使用 TypeScript?
TypeScript 为 JavaScript 提供了可选的静态类型。静态类型的主要好处是在构建时检查和发现类型错误,所以代码部署到生产环境后运行更稳定。
环境准备
Node 版本 >= 16
初始化项目
mkdir node-ts
cd node-ts
npm init -y
安装 typescript
npm i typescript -D
初始化 ts 配置文件
npx tsc --init
安装 @tsconfig/node16
npm i @tsconfig/node16 -D
@tsconfig/node16
为 TypeScript 团队为 Node.js v16 提供的基本配置。
tsconfig.json 中增加一下配置
{
"extends": "@tsconfig/node16/tsconfig.json",
"include": ["src"],
"exclude": ["node_modules"]
}
编写 ts 文件
src 下新建 index.ts 文件
const bar = 'bar'
console.log(bar)
通过 npx tsc
运行。js 文件是编译出来了,但是控制台报出错误:
TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
发生此错误是因为在 tsconfig.json
配置文件和 @tsconfig/node16
的配置中 compilerOptions
没有设置 lib
选项。该选项包含对象的类型定义和其他特定于浏览器的 API。将 "dom", "ESNext"
添加到 lib 中解决。但这不是 Node.js 项目的正确解决方案。正确的方式是安装 Node API 的类型定义,使 TypeScript 编译器可以理解和验证所有内置的 Node.js API。
安装 @types/node
npm i @types/node -D
安装完成后再次运行 npx tsc
错误消失。
如果要更改 js 文件的输出位置,可以在文件中 tsconfig.json 中指定 outDir 选项,编译后 js 文件将输出到 dist 目录下。
创建一个 http 服务感受下 ts 的类型检查和提示:
// index.ts
import http from 'http'
const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('hello ts node!')
})
server.listen(3000, () => {
console.log('runing...')
})
因为 node 运行时不能直接 ts 文件,所以需要将 ts 文件编译成 js 执行。运行 npx tsc
然后 node dist/index.js
,就可以访问 http://localhost:3000/
了。
这种方式每次改动代码都要编译,然后执行 js 文件。
可以通过 tsc -w
监听文件的变化,然后使用nodemon工具监听js文件的变化。有点繁琐。
使用 ts-node 直接执行 ts 文件
通过 ts-node CLI 在直接执行 ts 文件。安装 ts-node
npm i ts-node -D
执行 npx ts-node src/index.ts
,完全可行。
ts-node
作为 ts 源码 和 node 运行时之间的中间者。在执行生成的 js 代码之前转译源代码。这种执行速度更快。
另外 ts-node
启用的功能是将现代 esm 语法转换为 CommonJS 语法。这意味着在使用时 ts-node,您可以在代码中通 import 而不是 require 使用 node 模块。
package.json 中新增脚本:
{
"scripts": {
"dev": "ts-node src/index.ts"
}
}
使用 tsc-node-dev
可以监听文件的变化,当文件内容变化后重新编译并运行。
{
"scripts": {
"dev": "ts-node-dev src/index.ts"
}
}
ts 与第三方 npm 集成
当使用 npm 上的 node 模块时,可能需要额外的配置才能编译项目。
因为我们遇到的大多数模块都是用 js 编写的,因此 ts 无法确定方法的类型。模块中的所有内容都隐式为 any。
以 express 为例:
安装 express
npm i express
import express from 'express'
const app = express()
app.get('/', function (req, res) {
res.send('Hello World')
})
app.listen(3000)
会抛出错误: 无法找到模块“express”的声明文件,而且由于 tsconfig.json 的 strict 选项为 true,因此也启用了 noImplicitAny 编译器选项。使得 ts 在无法确定值的类型时会报错而不是进行推断类型。 所以 req 和 res 报错。
可以为模块提供类型声明文件来修复此错误。DefinitiveTyped GitHub 上提供了许多流行的 npm 包的类型定义。通过 @types
作用域安装包的类型定义。安装 express 的类型定义文件:
npm install @types/express -D
再次运行 npx ts-node src/index.ts
就成功了。
这种方式无法实时监听文件的变化,可以使用 nodemon
,通过 tsc -w
监听 ts 文件,变化后重新编译成 js,nodemon 监听到 js 变化后重新执行 js。
使用 ESLint 对 ts 进行检查
安装 ESLint
npm i eslint -D
要 eslint 对 ts 的检验,需要 eslint 的 ts 插件,@typescript-eslint/parser @typescript-eslint/eslint-plugin
:
npm i @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
根目录创建 .eslintrc.js 配置文件,配置内容为:
module.exports = {
env: {
node: true,
es2021: true
},
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: ['@typescript-eslint']
}
package.json 中添加 lint 脚本:
{
"scripts": {
"lint": "eslint . --fix"
}
}
代码中新增:
let bar = 'bar'
console.log(bar)
执行 npm run lint
,let 改成 const,eslint 生效。
为了防止 ESLint dist 下的 js 文件检查,创建 .eslintignore
文件,并将 dist 添加进去。因为 node_modules
文件夹中的所有内容以及以点字符开头的文件或文件夹(eslint 配置文件除外)都会被自动忽略,因此无需在 .eslintignore 文件中设置。
部署到生产环境
虽然 ts-node 在生产环境中使用也是是安全的。但为了减少服务器的启动时间以及减少额外的内存使用,最好预先编译源文件。将编译后的 js 部署到生产环境。