nodejs項目升級為ts



title: nodejs項目升級為ts
date: 2020-01-12
categories:

  • frontend
    tags:
  • nodejs
  • typescript

前言

最近把之前自用的一個小型nodejs框架(koa2+mongo)升級為了ts,在此記錄一下大致步驟。

安裝typescript

直接使用npm 安裝

npm i -S -D typescript

建議不要只安裝到全局,避免不同機器上的typescript版本不一致。
安裝完之后,我們新建一個tsconfig.json(或者tsc init),這是我的內容:

{
  "compilerOptions": {
    // "incremental": true,                   /* 增量編譯 提高編譯速度*/
    "target": "ES2017",                       /* 編譯目標ES版本*/
    "module": "commonjs",                     /* 編譯目標模塊系統*/
    // "lib": [],                             /* 編譯過程中需要引入的庫文件列表*/
    "declaration": true,                      /* 編譯時創建聲明文件 */
    "outDir": "dist",                         /* ts編譯輸出目錄 */
    "baseUrl": "src",
    "paths": {
        "@/*": ["*"],
    },
    // "importHelpers": true,                 /* 從tslib導入輔助工具函數(如__importDefault)*/
    "strict": false,                           /* 嚴格模式開關 等價於noImplicitAny、strictNullChecks、strictFunctionTypes、strictBindCallApply等設置true */
    "noImplicitAny": false,
    "noUnusedLocals": false,                   /* 未使用局部變量報錯*/
    "noUnusedParameters": false,               /* 未使用參數報錯*/
    "noImplicitReturns": true,                /* 有代碼路徑沒有返回值時報錯*/
    "noFallthroughCasesInSwitch": true,       /* 不允許switch的case語句貫穿*/
    "moduleResolution": "node",               /* 模塊解析策略 */
    "typeRoots": [                            /* 要包含的類型聲明文件路徑列表*/
      "./typings",
      "./node_modules/@types"
      ],                      
    "allowSyntheticDefaultImports": true,    /* 允許從沒有設置默認導出的模塊中默認導入,僅用於提示,不影響編譯結果*/
    "esModuleInterop": true                  /* 允許編譯生成文件時,在代碼中注入工具類(__importDefault、__importStar)對ESM與commonjs混用情況做兼容處理*/

  },
  "include": [                                /* 需要編譯的文件 */
    "src/**/*.ts",
    "typings/**/*.ts"
  ],
  "exclude": [                                /* 編譯需要排除的文件 */
    "node_modules/**"
  ],
}
  

集成eslint代碼規范

其實ts最早都是使用tslint,但2019開始ts官方決定投奔eslint,嗯,所以我們肯定也用eslint了。

1.安裝依賴

首先安裝eslint以及ts的parser、plugin:

npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D

說白了,就是eslint需要配置ts的parser、以及ts的規則,這是我的.eslintrc.js文件內容:

module.exports = {
    'parser':'@typescript-eslint/parser',
    'extends': ['standard', 'plugin:@typescript-eslint/recommended'],
    'env': {'node': true},
    'rules':{
        '@typescript-eslint/ban-ts-ignore': 0
    }
}

extends里面很明顯多了一個standard,這是因為@typescript-eslint/recommended的規則並不完善,所以我們還需要使用eslint的standard規則,安裝以下依賴:

npm i eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard -D

除了eslint-config-standard,后面幾個都是standard的依賴
rules是用來覆蓋規則的,我這里加了一個ban-ts-ignore,因為我有的地方要寫ts-ignore,不加他就報錯了

都配置好后,我們再來package.json的scripts中追加兩行命令:

"lint": "eslint --ext .js --ext .ts src/",
"lint-fix": "eslint --fix --ext .js --ext .ts src/",

lint用來檢測代碼格式問題,lint-fix用來自動修復代碼格式

2.配置vscode

ts的標配,毫無疑問是vscode。
vscode中需要安裝eslint插件(插件搜索安裝即可),安裝完成之后 首選項-> 設置 -> 擴展 -> eslint,我們直接點擊 在settings.json 中編輯 ,打開編輯文件進行編輯,這是我的配置文件(備注中有追加說明):

{
    "workbench.colorTheme": "One Dark Pro",
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true    // 保存文件時,自動eslint修復格式
    },
    "eslint.enable": true,  // 開啟eslint
    "eslint.options": {
        "extensions": [ // eslint檢測的文件格式
            ".js",
            ".ts",
            ".tsx",
        ],
    },
    "eslint.validate": [    // eslint檢測的語法
        "javascript",
        "javascriptreact",
        "typescript",
        "typescriptreact",
    ]
}

這里需要說明的是,eslint插件2.0.4開始,保存時自動修復文件是通過 editor.codeActionsOnSave 進行配置了,以前是 "eslint.autoFixOnSave": true 。
描述里還說只要在eslint中正確配置了ts,就不需要在eslint.validate中配置了,不過我都不管了,還是統統加上了。

源碼調整

這部分就略繁瑣了,需要把原代碼全部修改成ts,讓我們一步步來

1.修改文件后綴

把原來的js文件全部改成ts,我這里是直接寫了段node代碼來干這個事情:

const fs = require('fs')
const path = require('path')

function renameFile (dir) {
  fs.access(dir, function (err) {
    if (err) {
      console.log('目錄不存在')
    }
    _rename(dir)
  })

  function _rename (dir) {
    fs.readdir(dir, function (err, paths) {
      if (err) {
        console.log(err)
      } else {
        paths.forEach(function (curPath) {
          console.log(`查找到${curPath}`)
          const _src = dir + '/' + curPath
          const _dist = dir + '/' + curPath.replace('.js', '.ts')
          fs.stat(_src, function (err, stat) {
            if (err) {
              console.log(err)
            } else {
              // 判斷是文件還是目錄
              if (stat.isFile()) {
                if (curPath.endsWith('.js')) {
                  fs.rename(_src, _dist, function (err) {
                    if (err) {
                      console.log(err)
                    } else {
                      console.log(`${_src} ==> ${_dist}`)
                    }
                  })
                }
              } else if (stat.isDirectory()) {
                // 當是目錄是,遞歸復制
                _rename(_src)
              }
            }
          })
        })
      }
    })
  }
}

renameFile(path.join(__dirname, 'src'))

跑完之后把src目錄下面的js文件全部改成ts文件

2.修改代碼

主要是原來的require全部改成es的import,這里我是寫了一個正則,搜索替換的:

const ([^ ]+) = require\(([^)]+)\)  // from
import $1 from $2   // to

你如果對自己代碼有自信,也可以在上一步的node腳本中,讀取文件內容,然后替換掉。

關於import這一塊就比較有意思了,我們來看一段對比:

// 編譯前:
import path from 'path'
import fsTool from '@/util/fs-tool'

// 編譯后:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const fs_tool_1 = __importDefault(require("@/util/fs-tool"));

顯然,ts追加了一個 __importDefault函數,這就是ts的esModuleInterop編譯選項的作用,對es和commonjs的模塊做兼容處理:

  • ts編譯的時候,給當前模塊插入一個屬性: __esModule: true
  • 導入的時候先判斷該模塊是否有__esModule
  • 如果為true,判定為es模塊,直接導出
  • 如果為false,判定為commonjs模塊,作為default導出:{ default: mod }

    參考文章里認為這里會有一個陷阱,所以需要打開allowSyntheticDefaultImports編譯選項。的確,如果一個模塊是用ts寫的,或者自己就是追加了__esModule屬性,但同時exports又沒有default,最終 __importDefault導出來的會是一個空值。
    但我認為自己追加__esModule屬性不太可能,用ts寫的沒有export default但又import default時,ts是會報錯的,所以我認為這種情況可以忽略。

調試

1.chrome中調試

在package.json的scripts中追加以下命令:

"dev": "nodemon --watch src -e ts,tsx,js,json --exec node --inspect -r ts-node/register ./src/app.ts",

node調試的命令是 --inspect選項,nodemon用來監測到文件改動時自動重啟,ts-node用來調試ts代碼

以上,就能直接在chrome的開發者工具中調試了,如果有不清楚的可以自己去搜索node --inspect,這里就不做貼圖說明了

vscode中調試

.vscode/launch.json填入以下內容:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "attach",
            "name": "Node: Nodemon",
            "processId": "${command:PickProcess}",
            "restart": true,
            "protocol": "inspector",
        }
    ]
}

打開debug面板,選擇剛才設置的Node: Nodemon,點擊調試並運行,即可開始在vscode中調試(需要先npm run dev把項目運行起來):

說明

在查閱過的資料中,京東社交電商部的這篇文章幫助最大:Node.js項目TypeScript改造指南,非常感謝!
本文也是在此基礎上有部分更新以及補充。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM