徹底解決 TypeScript 報錯:“無法重新聲明塊范圍變量”的問題


背景

當使用 TypeScript + TSlint + Babel + Jest 搭建開發環境時,在開發過程中偶爾會被 IDE 提示「無法重新聲明塊范圍變量」,從而導致編譯出錯,報錯圖示如下:

相關開發環境配置如下:

  • typescript: ^3.5.3
  • tslint: ^5.19.0
  • babel: ^7.0.0
  • jest: ^24.9.0
  • ts-jest: ^24.0.2

解決方案

之所以 tslint 會提示這個錯誤,是因為在 Commonjs 規范里,沒有像 ESModule 能形成閉包的「模塊」概念,所有的模塊在引用時都默認被拋至全局,因此當再次聲明某個模塊時,TypeScript 會認為重復聲明了兩次相同的變量進而拋錯。

對於這個問題,最簡單的解決方法是在報錯的文件底部添加一行代碼:export {}。這行代碼會「欺騙」tslint 使其認為當前文件是一個 ESModule 模塊,因此不存在變量重復聲明的可能性。當使用這個方法時,記得這樣配置你的 tsconfig.json 文件:

{
  "include": ["src", "demo"],
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "esModuleInterop": true, // important!
    "target": "esnext",
    "strict": true,
    "outDir": "app",
    "declaration": true,
    "sourceMap": true
  }
}

其中 esMoudleInterop 這個配置允許文件中出現 export 關鍵字。

問題 2

當你以為已經萬事大吉的時候,你會發現第二個問題又浮出水面:你無法執行編譯后的 JavaScript 代碼!是的,因為 babel 雖然能夠幫你成功轉譯了 TypeScript 代碼,但是並沒有幫你去掉,你 hack 上的 export 關鍵字,因此 node 會由於無法識別該關鍵字而報錯。

此時你有兩個選擇:

刪除 export 關鍵字,忍受惱人的 IDE 提示,強行讓 babel 編譯;

幸運的是,這樣做真的行得通,因為 tslint 只是一個 “lint”,它只負責提示你哪里有問題,你可以強行忽略它。但是,當你使用 Jest 配合 TypeScript 進行測試時這樣做就行不通了,因為 Jest 會把 tslint 發現的錯誤當成無法原諒的錯誤告訴你,這意味着,你別想開開心心的測試你的代碼,當然你還可以選擇第二種解法:

寫一個 babel 插件,讓 babel 轉譯時去除 export 關鍵字;

這樣你的 node 可以識別轉譯后的代碼,你的 Jest 也不再會抱怨什么,兩全其美!然而,你真的想要專門為此寫一個插件嗎?

如果你的第一反應和我一樣是腦海中一個大大的 「NO!!!」,你應該繼續往下看了,其實我們還有第三個方案:)

終極解決方案

實際上,已經有一個 babel 插件可以滿足我們得需求了:@babel/plugin-transform-modules-commonjs ,這就是我們一直夢寐以求的東西。正如插件名所暗示的,它可以將 ESModule 模塊轉換為符合 Commonjs 規范的代碼,而經過我的測試,當遇到 export {} 這樣的表達式時,其轉譯的方案是:「直接忽略」!這正是我們想要的效果!

就這樣,在你的 babel.config.js 中加入這個插件,TSlint 不會再抱怨什么,Jest 能夠乖乖測試,Node 也不會朝你大吼 "What the * export !!",整個世界都清凈了。

最后,再分享一下我的全套相關配置,希望你們不再為這個問題感到困擾 😉:

tsconfig.json

{
  "include": ["src", "demo"],
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "esModuleInterop": true,
    "target": "esnext",
    "strict": true,
    "outDir": "app",
    "declaration": true,
    "sourceMap": true
  }
}

jest.config.js

module.exports = {
  roots: ['<rootDir>/src'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  testPathIgnorePatterns: ['/node_moudles/', './src/utils/test.ts'],
}

babel.config.js

module.exports = {
  presets: ['@babel/typescript'],
  plugins: [
    '@babel/plugin-transform-modules-commonjs',
    '@babel/proposal-class-properties',
    '@babel/proposal-object-rest-spread',
  ],
}


免責聲明!

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



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