為什么使用 TypeScript?
TypeScript 為 JavaScript 提供了可選的靜態(tài)類型。靜態(tài)類型的主要好處是在構(gòu)建時檢查和發(fā)現(xiàn)類型錯誤,所以代碼部署到生產(chǎn)環(huán)境后運行更穩(wěn)定。
環(huán)境準備
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'.
發(fā)生此錯誤是因為在 tsconfig.json 配置文件和 @tsconfig/node16 的配置中 compilerOptions 沒有設(shè)置 lib 選項。該選項包含對象的類型定義和其他特定于瀏覽器的 API。將 "dom", "ESNext" 添加到 lib 中解決。但這不是 Node.js 項目的正確解決方案。正確的方式是安裝 Node API 的類型定義,使 TypeScript 編譯器可以理解和驗證所有內(nèi)置的 Node.js API。
安裝 @types/node
npm i @types/node -D
安裝完成后再次運行 npx tsc 錯誤消失。
如果要更改 js 文件的輸出位置,可以在文件中 tsconfig.json 中指定 outDir 選項,編譯后 js 文件將輸出到 dist 目錄下。
創(chuàng)建一個 http 服務(wù)感受下 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 執(zhí)行。運行 npx tsc 然后 node dist/index.js,就可以訪問 http://localhost:3000/了。
這種方式每次改動代碼都要編譯,然后執(zhí)行 js 文件。
可以通過 tsc -w 監(jiān)聽文件的變化,然后使用nodemon工具監(jiān)聽js文件的變化。有點繁瑣。
使用 ts-node 直接執(zhí)行 ts 文件
通過 ts-node CLI 在直接執(zhí)行 ts 文件。安裝 ts-node
npm i ts-node -D
執(zhí)行 npx ts-node src/index.ts,完全可行。
ts-node 作為 ts 源碼 和 node 運行時之間的中間者。在執(zhí)行生成的 js 代碼之前轉(zhuǎn)譯源代碼。這種執(zhí)行速度更快。
另外 ts-node 啟用的功能是將現(xiàn)代 esm 語法轉(zhuǎn)換為 CommonJS 語法。這意味著在使用時 ts-node,您可以在代碼中通 import 而不是 require 使用 node 模塊。
package.json 中新增腳本:
{
"scripts": {
"dev": "ts-node src/index.ts"
}
}
使用 tsc-node-dev 可以監(jiān)聽文件的變化,當文件內(nèi)容變化后重新編譯并運行。
{
"scripts": {
"dev": "ts-node-dev src/index.ts"
}
}
ts 與第三方 npm 集成
當使用 npm 上的 node 模塊時,可能需要額外的配置才能編譯項目。
因為我們遇到的大多數(shù)模塊都是用 js 編寫的,因此 ts 無法確定方法的類型。模塊中的所有內(nèi)容都隱式為 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 就成功了。
這種方式無法實時監(jiān)聽文件的變化,可以使用 nodemon,通過 tsc -w 監(jiān)聽 ts 文件,變化后重新編譯成 js,nodemon 監(jiān)聽到 js 變化后重新執(zhí)行 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
根目錄創(chuàng)建 .eslintrc.js 配置文件,配置內(nèi)容為:
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)
執(zhí)行 npm run lint,let 改成 const,eslint 生效。
為了防止 ESLint dist 下的 js 文件檢查,創(chuàng)建 .eslintignore 文件,并將 dist 添加進去。因為 node_modules 文件夾中的所有內(nèi)容以及以點字符開頭的文件或文件夾(eslint 配置文件除外)都會被自動忽略,因此無需在 .eslintignore 文件中設(shè)置。
部署到生產(chǎn)環(huán)境
雖然 ts-node 在生產(chǎn)環(huán)境中使用也是是安全的。但為了減少服務(wù)器的啟動時間以及減少額外的內(nèi)存使用,最好預先編譯源文件。將編譯后的 js 部署到生產(chǎn)環(huán)境。