类属性不存在

时间:2018-11-11 02:19:26

标签: typescript

我一直在寻找一个问题,无法找到解决方案。我已将代码简化到最低限度,但仍然无法弄清楚。

我为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

有什么主意吗?

非常感谢!

1 个答案:

答案 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
  }
}

(请注意,allowJsdeclaration不兼容,因此必须删除。如果需要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输出。)