简体   繁体   English

TypeScript 模块导入和 WebPack

[英]TypeScript Module Importing & WebPack

I'm having some trouble getting WebPack to inject imported dependencies for a project written in TypeScript.我在让 WebPack 为用 TypeScript 编写的项目注入导入的依赖项时遇到了一些麻烦。 My first issue is getting TypeScript to recognize the imported module.我的第一个问题是让 TypeScript 识别导入的模块。

I have a header.ts file that declares a module that is nested under vi.input and exports a VIInputDirective class.我有一个header.ts文件,它声明了一个嵌套在vi.input下的模块,并导出了一个VIInputDirective类。 In the main.ts file, I try to import the exported VIInputDirective class from the header.ts file, but can't seem to get TypeScript to recognize it.main.ts文件中,我尝试从header.ts文件导入导出的VIInputDirective类,但似乎无法让 TypeScript 识别它。

header.ts头文件

module vi.input.header {
  import IDirective = angular.IDirective;

  export class VIInputDirective implements IDirective {
    ...
  }
}

main.ts主文件

import {VIInputDirective} from "./header.ts"; // Nothing imported; Cannot Resolve issue
VIInputDirective.whatever(); // Does not work

material.dt.s材料.dt.s

declare module vi.input {
  ...
}

If I swap import {VIInputDirective} from "./header.ts";如果我交换import {VIInputDirective} from "./header.ts"; in the main.ts file with import VIMHeaderDirective = vi.input.header.VIInputDirective;main.ts文件中使用import VIMHeaderDirective = vi.input.header.VIInputDirective; it works fine, but then webpack on transpile/inject gives me the following error:它工作正常,但是 webpack 在 transpile/inject 上给了我以下错误:

VM1469:1Uncaught ReferenceError: vi is not defined

I've tried exporting the vi.input.header module directly (ie export module vi.input.header) but that did not work.我试过直接导出vi.input.header模块(即导出模块 vi.input.header),但没有奏效。 I also tried using the reference sytnax to include the file but that did not work either: ///<reference path="file_path"/> .我还尝试使用参考 sytnax 来包含文件,但这也不起作用: ///<reference path="file_path"/>

It's an issue with the nesting of modules because if I remove the module and directly export the VIInputDirective class, it works fine.这是模块嵌套的问题,因为如果我删除模块并直接导出 VIInputDirective 类,它可以正常工作。 However, I want to keep it in a nested module.但是,我想将它保留在嵌套模块中。

You are not using modules.您没有使用模块。 Modules have one or more top level import or export statements.模块具有一个或多个顶级importexport语句。 Instead, you are using the global namespace and creating subnamespaces of the global namespace to organize your program.相反,您正在使用全局命名空间并创建全局命名空间的子命名空间来组织您的程序。 This is called the revealing module pattern.这称为揭示模块模式。 It does not involve using modules.它不涉及使用模块。

Unfortunately, TypeScript used to refer to this pattern as using Internal Modules .不幸的是,TypeScript 过去常常将这种模式称为使用内部模块 This terminology has since been deprecated and use of the module x {} syntax (note the lack of " s around x ) is strongly discouraged. The language has introducing a synonymous keyword namespace , to reduce confusion.此术语已被弃用,并且强烈建议不要使用module x {}语法(注意x周围缺少" s )。该语言引入了同义关键字namespace ,以减少混淆。

JavaScript loaders and bundlers like Webpack, RequireJS, and SystemJS work with modules, that is what TypeScript referred to as external modules . JavaScript 加载器和打包器(如 Webpack、RequireJS 和 SystemJS)与模块一起工作,这就是 TypeScript 所说的外部模块

To clarify the following constructs you mention are not module related为了澄清你提到与模块有关以下构造

  1. top level, non-exported module / namespace syntactic declarations顶级非导出module / namespace语法声明

    module vi.input.header { ... }

    this would now be written as这现在可以写成

    namespace vi.input.header { ... }

    in order to minimize confusion but, irregardless, the emit has always resulted in.为了尽量减少混淆,但无论如何,发射总是导致。

     var vi; (function (vi) { var input; (function (input) { var header; (function (header) { })(header = input.header || (input.header = {})); })(input = vi.input || (vi.input = {})); })(vi || (vi = {}));

    Note this mutates the global scope in a pattern commonly used by various libraries.请注意,这会以各种库常用的模式改变全局范围。 namespace s (formerly called internal modules) like the one above have the interesting property that multiple files can contribute to their contents, and this is in fact their primary purpose. namespace (以前称为内部模块)有一个有趣的特性,即多个文件可以为其内容做出贡献,而这实际上是它们的主要目的。 This explains the degree of nesting and the conditional assignments to variables in the emit above.这解释了上面发射中的嵌套程度和对变量的条件赋值。 This has nothing to do with using Modules .这与使用Modules无关。

  2. import assignments that reference namespace members such as import引用namespace成员的赋值,例如

    import IDirective = angular.IDirective;

    do not qualify as top level import declarations, and thus do not cause their containing file to be considered a module.不符合顶级import声明的条件,因此不会导致它们的包含文件被视为模块。 This is true even if they are placed at the top level of a file.即使它们位于文件的顶层也是如此。 The reason is that module systems, be they AMD, CommonJS, System, or ES2015, all use strings as module specifiers, importing from such strings;原因是模块系统,无论是 AMD、CommonJS、System 还是 ES2015,使用字符串作为模块说明符,从这些字符串导入; which incidentally may represent file paths, urls, resolved simple names, or synthetic module ids.顺便说一下,它可能代表文件路径、url、解析的简单名称或合成模块 ID。

Again, the import name = qualified.global.name statements in your code are a TypeScript specific feature that is unrelated to Modules .同样,代码中的import name = qualified.global.name语句是与Modules无关的 TypeScript 特定功能。 They can be quite useful for aliasing nested types and values, but modules they do not make.它们对于嵌套类型和值的别名非常有用,但它们不制作模块。

Now here is where it gets interesting, and where it intersects with your specific question namespace s can be used, and at times quite elegantly, from within external modules, but their semantics are very different现在,这里是它变得有趣,并在那里与您的具体问题相交namespace s可以使用,而且有时相当漂亮,从外部模块,但它们的语义有很大的不同

Consider考虑

services.ts服务.ts

export namespace app {
  export class SomeService { }
}

which compiles into the following JavaScript编译成以下 JavaScript

export var app;
(function (app) {
    class SomeService {
    }
    app.SomeService = SomeService;
})(app || (app = {}));

main.ts主文件

export namespace app {
  export function bootstrap() { }
}

which compiles into the following JavaScript编译成以下 JavaScript

export var app;
(function (app) {
    function bootstrap() { }
    app.bootstrap = bootstrap;
})(app || (app = {}));

Both of above are external modules, that is true modules, that use namespaces as internal code organization mechanisms but they key takeaway is that they do not contribute to a shared app namespace, each having their own, file scoped app variable.以上两个都是外部模块,也就是真正的模块,它们使用命名空间作为内部代码组织机制,但它们的关键在于它们构成共享app命名空间,每个命名空间都有自己的文件范围的app变量。 Neither has implicit access to the other's members, the declarations of namespace app do not merge across files, and their having a similar internal naming scheme is incidental to their modularity.两者都没有对对方成员的隐式访问, namespace app的声明不会跨文件合并,并且它们具有类似的内部命名方案是它们的模块化附带的。

So how does all of this relate to your question and to the suggestions you tried to apply?那么所有这些与您的问题和您尝试应用的建议有何关系?

Let us see让我们看看

headers.ts头文件.ts

module vi.input.header {
  import IDirective = angular.IDirective;

  export class VIInputDirective implements IDirective {
    static whatever() { }
  }
}

This file is not a module, as explain above, and uses the global namespace to expose its declarations.如上所述,该文件不是模块,而是使用全局命名空间来公开其声明。 If we were writing this today, we would use the namespace keyword instead of the module keyword, by convention.如果我们今天写这个,按照惯例,我们会使用namespace关键字而不是module关键字。

main.ts主文件

import {VIInputDirective} from "./header.ts"; // Nothing imported; Cannot Resolve issue
VIInputDirective.whatever(); // Does not work

Indeed neither line works because you are importing headers.ts as if it were a module, but as we have just seen it is not a module.确实,这两行都不行,因为您将headers.ts当作模块导入,但正如我们刚刚看到的,它不是模块。 Furthermore, the first line, which is a top level import statement that imports from a module specifier string, ironically makes main.ts , itself, a module.此外,具有讽刺意味的是,第一行是从模块说明符字符串import的顶级import语句,它使main.ts本身成为一个模块。

In short the two styles do not mix well, and sharing code between them is not simple and is not something you should likely try to do (I have left the UMD format out of this answer to try to keep it relatively straightforward).简而言之,这两种风格不能很好地混合,并且在它们之间共享代码并不简单,也不是您应该尝试做的事情(我已将 UMD 格式排除在此答案之外,以使其相对简单)。

Now we come full circle现在我们回到了原点

strong text强文本

declare module vi.input {
    ....
}

If I swap import {VIInputDirective} from "./header.ts";如果我交换 import {VIInputDirective} from "./header.ts"; in the main.ts file with import VIMHeaderDirective = vi.input.header.VIInputDirective;在 main.ts 文件中使用 import VIMHeaderDirective = vi.input.header.VIInputDirective; it works fine, but then webpack on transpile/inject gives me the following error:它工作正常,但是 webpack 在 transpile/inject 上给了我以下错误:

Indeed as explained above.确实如上文所述。 This import , which does not target a module specifier string, and the absence of any others top level imports or exports, changes main.ts such that it is no longer a module.import不针对模块说明符字符串,并且没有任何其他顶级导入或导出,更改main.ts使其不再是模块。 This causes TypeScript to TypeCheck it correctly, global variables referencing each other using import = namespace.value is perfectly legitimate, but these are not modules and JavaScript tools, such as Webpack, operate on modules.这会导致 TypeScript 正确地对其进行类型检查,使用import = namespace.value相互引用的全局变量是完全合法的,但这些不是模块和 JavaScript 工具,例如 Webpack,对模块进行操作。

So how would you write this app in this brave new world?那么,在这个美丽的新世界中,您将如何编写这个应用程序? Since you are using a module bundling tool, Webpack, you would write it using proper modules all the way down.由于您使用的是模块捆绑工具 Webpack,因此您可以一直使用适当的模块来编写它。

main.ts主文件

import {VIInputDirective} from "./header.ts";
VIInputDirective.whatever();

headers.ts头文件.ts

import {IDirective} from 'angular';

export class VIInputDirective implements IDirective {
  static whatever() { }
}

material.d.ts no longer looks as it did when you wrote this, as it has been updated to work with proper modules. material.d.ts不再像您编写此文件时那样,因为它已更新为可与适当的模块一起使用。 If you need to reference something from it, use the module syntax如果您需要从中引用某些内容,请使用模块语法

my-dialog-options.ts我的对话框选项.ts

import {material} from 'angular';

const dialogOptions: material.IDialogOptions = { ... };

export default dialogOptions;

I have tried not to oversimplify but some hand waving has been necessary to avoid writing a novella on the subject, but I believe I hope to have covered and conveyed the key points.我试图不过度简化,但为了避免写一篇关于这个主题的中篇小说,必须挥手致意,但我相信我希望已经涵盖并传达了关键点。

You are looking for namespaces not module, so.您正在寻找名称空间而不是模块,所以。

header.ts头文件

export namespace vi.input.header {
  export class VIInputDirective {

  }
}

main.ts主文件

import { vi } from "./header.ts"; 
var foo = new vi.input.header.VIInputDirective();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM