彻底解决 TypeScript 报错:“无法重新声明块范围变量”的问题
<h2 id="背景">背景</h2><p>当使用 TypeScript + TSlint + Babel + Jest搭建开发环境时,在开发过程中偶尔会被 IDE 提示「无法重新声明块范围变量」,从而导致编译出错,报错图示如下:</p>
<p><img src="https://img2018.cnblogs.com/blog/893617/201911/893617-20191114141817706-843328079.png" alt="" loading="lazy"></p>
<p>相关开发环境配置如下:</p>
<ul>
<li>typescript: ^3.5.3</li>
<li>tslint: ^5.19.0</li>
<li>babel: ^7.0.0</li>
<li>jest: ^24.9.0</li>
<li>ts-jest: ^24.0.2</li>
</ul>
<h2 id="解决方案">解决方案</h2>
<p>之所以 tslint 会提示这个错误,是因为在 Commonjs 规范里,没有像 ESModule 能形成闭包的「模块」概念,所有的模块在引用时都默认被抛至全局,因此当再次声明某个模块时,TypeScript 会认为重复声明了两次相同的变量进而抛错。</p>
<p>对于这个问题,最简单的解决方法是在报错的文件底部添加一行代码:<code>export {}</code>。这行代码会「欺骗」tslint 使其认为当前文件是一个 ESModule 模块,因此不存在变量重复声明的可能性。当使用这个方法时,记得这样配置你的 <code>tsconfig.json</code> 文件:</p>
<pre><code class="language-json">{
"include": ["src", "demo"],
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"esModuleInterop": true, // important!
"target": "esnext",
"strict": true,
"outDir": "app",
"declaration": true,
"sourceMap": true
}
}
</code></pre>
<p>其中 <code>esMoudleInterop</code> 这个配置允许文件中出现 <code>export</code> 关键字。</p>
<h2 id="问题-2">问题 2</h2>
<p>当你以为已经万事大吉的时候,你会发现第二个问题又浮出水面:你无法执行编译后的 JavaScript 代码!是的,因为 babel 虽然能够帮你成功转译了 TypeScript 代码,但是并没有帮你去掉,你 hack 上的 <code>export</code> 关键字,因此 node 会由于无法识别该关键字而报错。</p>
<p>此时你有两个选择:</p>
<blockquote>
<p>删除 <code>export</code> 关键字,忍受恼人的 IDE 提示,强行让 babel 编译;</p>
</blockquote>
<p>幸运的是,这样做真的行得通,因为 tslint 只是一个 “lint”,它只负责提示你哪里有问题,你可以强行忽略它。但是,当你使用 Jest 配合 TypeScript 进行测试时这样做就行不通了,因为 Jest 会把 tslint 发现的错误当成无法原谅的错误告诉你,这意味着,你别想开开心心的测试你的代码,当然你还可以选择第二种解法:</p>
<blockquote>
<p>写一个 babel 插件,让 babel 转译时去除 <code>export</code> 关键字;</p>
</blockquote>
<p>这样你的 node 可以识别转译后的代码,你的 Jest 也不再会抱怨什么,两全其美!然而,你真的想要专门为此写一个插件吗?</p>
<p>如果你的第一反应和我一样是脑海中一个大大的 「NO!!!」,你应该继续往下看了,其实我们还有第三个方案:)</p>
<h2 id="终极解决方案">终极解决方案</h2>
<p>实际上,已经有一个 babel 插件可以满足我们得需求了:<code>@babel/plugin-transform-modules-commonjs</code> ,这就是我们一直梦寐以求的东西。正如插件名所暗示的,它可以将 ESModule 模块转换为符合 Commonjs 规范的代码,而经过我的测试,当遇到 <code>export {}</code> 这样的表达式时,其转译的方案是:「直接忽略」!这正是我们想要的效果!</p>
<p>就这样,在你的 <code>babel.config.js</code> 中加入这个插件,TSlint 不会再抱怨什么,Jest 能够乖乖测试,Node 也不会朝你大吼 "What the * export !!",整个世界都清净了。</p>
<p>最后,再分享一下我的全套相关配置,希望你们不再为这个问题感到困扰 😉:</p>
<h3 id="tsconfigjson">tsconfig.json</h3>
<pre><code class="language-json">{
"include": ["src", "demo"],
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"esModuleInterop": true,
"target": "esnext",
"strict": true,
"outDir": "app",
"declaration": true,
"sourceMap": true
}
}
</code></pre>
<h3 id="jestconfigjs">jest.config.js</h3>
<pre><code class="language-js">module.exports = {
roots: ['<rootDir>/src'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testPathIgnorePatterns: ['/node_moudles/', './src/utils/test.ts'],
}
</code></pre>
<h3 id="babelconfigjs">babel.config.js</h3>
<pre><code class="language-js">module.exports = {
presets: ['@babel/typescript'],
plugins: [
'@babel/plugin-transform-modules-commonjs',
'@babel/proposal-class-properties',
'@babel/proposal-object-rest-spread',
],
}
</code></pre>
</div>
<div id="MySignature" role="contentinfo">
👋 Hey!喜欢这篇文章吗?别忘了在下方👇 投票让我知道。<br><br>
来源:https://www.cnblogs.com/libinfs/p/11857187.html
頁:
[1]