【问题标题】:Typescript 2 — using ES6 import & require?Typescript 2 — 使用 ES6 导入和要求?
【发布时间】:2017-08-06 11:26:16
【问题描述】:

我在使用 ES6 模块的工作 Angular2 应用程序中有一个导出的 class

//File = init.todos.ts

export class Init {
   load() {
      ...
    }
}

我正在通过以下方式从另一个组件导入这个类:

//File = todo.service.ts

import { Init } from './init.todos'

它按预期工作。

但是,如果我将加载机制更改为 commonjs :

//File = init.todos.ts
export class Init {
    load() {
       ...
    }
}
 module.exports.Init = Init;

需要它:

//File = todo.service.ts
var  Init = require("./init.todos");

——我得到了这些错误:

...myApp/src/app/todo.service.ts (4,13): 找不到名称 'require'。) ...myApp/src/app/todo.service.ts (12,14): 类型“TodoService”上不存在属性“load”。)

问题:

如何使用 require 加载 commonjs 模块?

Tsconfig.json:

{
    "compileOnSave": false,
    "compilerOptions": {
        "outDir": "./dist/out-tsc",
        "baseUrl": "src",
        "module": "system",
        "sourceMap": true,
        "declaration": false,
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5",
        "typeRoots": [
            "node_modules/@types"
        ],
        "lib": [
            "es2016",
            "dom"
        ]
    }
}

这里是配置文件:

tslint.json

tsconfig.json

package.json

angular-cli.json

webpack.config.js

【问题讨论】:

  • 发生这种情况是因为您拥有"module": "system"。 SystemJS 中没有 require 函数。
  • 在 tsconfig 中,将 "module" 更改为 "commonjs" 并保留 ES6 语法,例如`从'./init.todos'导入{初始化}。
  • @BrunoGrieder 哦,所以导入的模块会有module.exports,但我仍然应该通过 es6 语法导入它?
  • 是的。 Typescript 不久前采用了 ES6 样式导入(1.5 或 1.7)。如果您使用 const x = require('blah'),您将使用 NodeJS/CommonJS 要求并且“丢失”类型,因为 x 将被映射到任何类型。这对于导入未键入的 JS 库可能很有用

标签: javascript angular typescript module webpack


【解决方案1】:

TypeScript 符合 ES6。在 TypeScript 源文件中使用 ES6 模块加载语法:

import  { SomeType } from './some.module';

当 TypeScript 编译为常规 JavaScript 时,您可以选择一个模块系统:

"compileOptions": {
    "module": "commonjs" (or system, umd, amd)
}

无论您选择何种目标模块系统,您都需要确保包含加载模块所需的脚本和配置。即 SystemJS、RequireJS 等。

作为练习,尝试针对不同的模块加载器,并检查 .js 文件 - 你会明白我的意思。

【讨论】:

    【解决方案2】:

    version 1.5 开始,Typescript 采用了 ES6 风格的导入。

    您可以保持代码“原样”

    //File = init.todos.ts
    
    export class Init {
       load() {
          ...
        }
    }
    
    //File = todo.service.ts
    
    import { Init } from './init.todos'
    

    通过将tsconfig.json 中的属性module 移动到commonjs,转译器将生成与commonjs 兼容的javascript,这将使您能够从整个nodeJS 生态系统中受益并做出贡献。

    "compileOptions": {
        ...
        "module": "commonjs"
        ...
    }
    

    如果您希望在 typescript 代码中导入现有的 NodeJS 模块,可能会发生一些事情

    • 该模块附带嵌入式 Typescript 定义文件(“package.json”中有一个“typings”条目)。在这种情况下,使用 ES6 语法导入模块就可以了(import * as blah from 'blah')。 immutable.js 就是这样一个库的一个很好的例子

    • 该模块不附带定义文件,但DefinitelyTyped 上有一个可用的,只是

      • 运行npm install @types/blah 以获取项目中的定义
      • 并照常导入模块:import * as blah from 'blah'
    • 模块没有定义文件

      • 您可以制作一个,只需将其命名为blah.d.ts 后将其添加到您的项目中。绝对类型有a guide这个
      • 您可以决定不输入,只需使用 NodeJS require() const blah = require('blah')blah 将输入 any(这实际上意味着 blah 选择不输入)

    (为了完整起见,我可以添加自 1.8 以来可用的 allowJS compiler option,但这本身就是一个完整的主题)

    【讨论】:

    • 很抱歉,您的意思是在第二种情况下:“该模块不附带定义文件,但DefiniteTyped 上有一个可用的文件,只是” - 我可以通过 "regular require" 导入它。正确的 ? (我的意思是确定我可以通过 ES6 import 获得它,但重点是 d.ts 允许 TS 识别 require ,不是吗?)
    • TS 不“识别需求”。拥有定义文件的重点是 JS 模块可以像用 typescript 编写一样使用,因此可以使用 typescript 导入语法导入,即 ES6 导入语法
    • 是的,这就是我的意思,它允许 TS 编译器识别新的d.ts'ed 关键字。 (如果我错了纠正我)。但我还是想问一下你提到的第二种情况。即使我添加了blah.d.ts,这是我的朋友用他的 blah 库 (commonjs) 发给我的 - Typescript 只会屏蔽错误 关于我将在我的代码中包含的require ('blah')。正确的 ?我的意思是我必须有另一个模块加载器(browserify/webpack)才能让最终的 JS 实际引用那些所需的文件。对吗?
    • 是的。您需要一个模块捆绑器,例如 browserify 或 webpack。使用 requireallowJs 您可能可以避免使用其中之一,但您会失去打字的好处
    • :-) (终于我开始明白了。) - 好吧,如果是这样 - 如果我在 tsconfig 中更改 module 有什么区别?我的意思是 - 首先它与 TS 有什么关系?从我的测试 - TS 搜索 es6 语法并转换它。 It doesn't care what you have in your code even if it's invalid。你知道我的误解在哪里吗? TS 与 tsconfig 的模块设置有什么关系?我的意思是由 webpack/browserify 来“要求”库(除了 es6 import built inTS)。
    【解决方案3】:

    你的问题实际上不是 require(...) 方法。它是你引用错误 Init。我想你想通过 require 来 lazy load 模块。但是你加载包含实例方法的类 @ 987654322@(它不是类方法,如果你想要你必须声明它为static)。所以你必须在使用它之前创建一个实例。默认情况下,require(...)返回任何

    declare function require(id: string): any;
    

    如果你想要 typescript 正确推断类型,你必须声明 require() 返回类型,例如:let foo:typeof Foo = require('foo')。我发现ts-jest 测试通过时module 是除system 之外的任何东西,因为它是延迟加载,不立即执行测试定义,所以我确定问题不在于模块。这是我的测试代码:

    测试

    //for inferer type for typescript
    //This leverages the reference-elision optimization,
    //so that the module is only loaded when needed.
    //so use InitClass as a type will skipped.
    import {Init as InitClass} from  "./init.todos";
    
    test("typescript dynamic load supports inferer types", () => {
        let Init: typeof InitClass = require("./init.todos").Init;
    
        let it = new Init();
        expect(it.load()).toEqual("bar");
    });
    

    【讨论】:

      猜你喜欢
      • 2018-11-17
      • 2017-03-11
      • 1970-01-01
      • 2019-12-18
      • 2018-02-02
      • 2015-08-13
      • 2015-11-05
      • 2020-01-11
      相关资源
      最近更新 更多