我一直在寻找一个问题,无法找到解决方案。我已将代码简化到最低限度,但仍然无法弄清楚。
我为Babel和Typescript设置了两个配置为相同的基本项目。我的一个项目实际上是一个模块,我将其导入另一个项目中。我的两个项目都用Typescript编码,但是在运行它们之前,每个项目都已转换为JS。
两个项目都具有以下Babel 7配置:
// babel.config.js
module.exports = function( api ) {
api.cache( () => process.env.NODE_ENV );
const presets = [
[
"@babel/env",
{
targets: {
node: "10",
browsers: "> 0.25%, not dead" // without this line, Babel doesn't transform the class and it works.
},
useBuiltIns: "usage",
}
],
"@babel/preset-typescript"
];
const plugins = [
// same result with or without this plugin
// the value of loose doesn't change anything
[ "@babel/plugin-proposal-class-properties", { loose: false } ]
];
return {
presets,
plugins,
};
};
两个项目都具有以下Typescript配置:
// tsconfig.json
{
"compilerOptions": {
"target": "ES5", // I tried multiples values for this one
"module": "commonjs",
"allowJs": true,
"noEmit": true,
"esModuleInterop": true
}
}
我的第一个测试是编译我的类并在没有任何模块的情况下运行它。 这是我的源代码:
//{ProjectRoot}/src/main.ts
class MOD {
public name:string;
constructor() {
this.name = "Foo";
}
getName():string {
return this.name;
}
}
const mod = new MOD();
console.log(MOD) // Output: [Function: MOD]
console.log(mod) // Output: MOD { name: 'Foo' }
console.log(mod.getName())// Output: Foo
我通过运行以下代码来编译并运行此源代码:
tsc && babel ./src --out-dir dist -x .js,.ts -D --verbose --delete-dir-on-start && node dist/main
一切正常,我在终端中获得了预期的输出。 当我查看dist / main.js时,可以看到该类已被编译器完全重写。
//{ProjectRoot}/dist/main.js
//... some code added by Babel ...
var MOD =
/*#__PURE__*/
function () {
function MOD() {
_classCallCheck(this, MOD);
_defineProperty(this, "name", void 0);
this.name = "Foo";
}
_createClass(MOD, [{
key: "getName",
value: function getName() {
return this.name;
}
}]);
return MOD;
}();
如果我在Babel配置中删除了browsers: "> 0.25%, not dead"
,则编译器仅添加_defineProperty函数,而其余代码保持原样。
然后我将相同的代码移至另一个项目,该项目模拟一个NPM模块(但位于项目根目录之外的另一个目录中)。
模块的源代码为:
// {moduleRoot}/src/main.ts
class MOD {
constructor() {
this.name = "Foo";
}
getName() {
return this.name;
}
}
module.exports = MOD;
使用与上述相同的命令编译模块。
然后在我的项目中,我将源代码修改如下:
//{ProjectRoot}/src/main.ts
//const Mod = require("../../module/dist/main"); // Works with require()
import Mod from "../../module/dist/main"; // Doesn't work with import
const mod = new Mod();
console.log(MOD) // Output: [Function: MOD]
console.log(mod) // Output: MOD { name: 'Foo' }
console.log(mod.getName())// Output: Foo
同样,它很棒。然后,我将require()
替换为import
,事情就不再起作用了。
如果在不进行类型检查的情况下进行编译,则将获得正确的输出,这意味着Node可以解释我的代码。
但是在进行类型检查时,Typescript给我以下错误:
src/main.ts:7:17 - error TS2339: Property 'getName' does not exist on type 'MOD'.
7 console.log(mod.getName())
我不明白为什么导入模块的方式会影响结果。 在我看来,Typescript编译器无法正确解析模块。
我已经用源代码和编译后的代码创建了一个Gist。
有什么主意吗?
非常感谢!
答案 0 :(得分:0)
由于项目正在导入module/dist/main
,并且在该路径中仅存在一个.js
文件,因此TypeScript编译器具有用于该导入的唯一类型信息是它可以从main.js
中提取的信息,这通常是不完整的。您可能应该做的是在模块上将tsc
与.d.ts
文件一起生成.js
文件。然后项目上的tsc
将使用.d.ts
文件。例如,将module/tsconfig.json
更改为以下内容:
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"rootDir": "src",
"outDir": "dist",
"declaration": true,
"emitDeclarationOnly": true,
"esModuleInterop": true
}
}
(请注意,allowJs
与declaration
不兼容,因此必须删除。如果需要allowJs
,请说明您的情况,我将尽力帮助您解决。 ),并使用以下命令编译模块:
rm -rf dist && tsc && babel ./src --out-dir dist -x .js,.ts -D --verbose
您还必须将module/src/main.ts
末尾的导出更改为:
export default MOD;
因为TypeScript编译器无法识别module.exports
文件中对.ts
的分配,并且Babel不支持export = MOD
。只要模块的所有使用方都使用默认导入,这就应该起作用。 (再次,如果您需要支持做简单的require
的消费者,请说明您的情况,我会做我能做的。)
如果您需要导出分配,由于您的.ts
文件将需要说export = MOD
,而Babel不接受,那么我唯一能找到的解决方案是在由tsc
,其中export = MOD
将被转换为module.exports = MOD
。在示例配置中,module/tsconfig.json
为:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"rootDir": "src",
"outDir": "ts-out",
"declarationDir": "dist",
"declaration": true,
"esModuleInterop": true
}
}
,构建命令为:
rm -rf dist ts-out && tsc && babel ./ts-out --out-dir dist -x .js,.ts -D --verbose
(这是假设您完全有理由使用Babel而不是直接使用tsc
的JavaScript输出。)