TypeScript模块导入&的WebPack

时间:2016-09-28 21:49:16

标签: javascript typescript webpack

我在使用TypeScript编写的项目中为WebPack注入导入的依赖项时遇到了一些麻烦。我的第一个问题是让TypeScript识别导入的模块。

我有一个 header.ts 文件,它声明一个嵌套在 vi.input 下的模块,并导出一个 VIInputDirective 类。在 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

declare module vi.input {
  ...
}

如果我将 main.ts 文件中的import {VIInputDirective} from "./header.ts";import VIMHeaderDirective = vi.input.header.VIInputDirective;交换,它可以正常工作,但是在transpile / inject上的webpack会出现以下错误:

VM1469:1Uncaught ReferenceError: vi is not defined

我已尝试直接导出 vi.input.header 模块(即导出模块vi.input.header),但这不起作用。我也尝试使用引用sytnax来包含文件,但这也不起作用:///<reference path="file_path"/>

模块嵌套存在问题,因为如果我删除模块并直接导出VIInputDirective类,它就可以正常工作。但是,我想将它保存在嵌套模块中。

2 个答案:

答案 0 :(得分:3)

您没有使用模块。模块具有一个或多个顶级importexport语句。相反,您正在使用全局命名空间并创建全局命名空间的子命名空间来组织您的程序。这被称为揭示模块模式。它不涉及使用模块。

不幸的是,TypeScript曾将此模式称为使用内部模块。此术语已被弃用,强烈建议不要使用module x {}语法(请注意"周围缺少x)。 该语言引入了同义关键字namespace,以减少混淆。

像Webpack,RequireJS和SystemJS这样的JavaScript加载器和捆绑器使用模块,这就是TypeScript所称的外部模块

为了澄清您提到的以下结构 模块相关

  1. 顶级非导出module / namespace句法声明

    module vi.input.header { ... }
    

    现在这将写为

    namespace vi.input.header { ... }
    

    为了尽量减少混淆,但无论如何,发射总是导致。

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

    请注意,这会以各种库常用的模式改变全局范围。像上面那样的namespace(以前称为内部模块)具有多个文件可以为其内容做出贡献的有趣属性,这实际上是它们的主要目的。这解释了嵌套的程度和上面发射中变量的条件赋值。这与使用模块无关。

  2. import指定namespace成员的作业,例如

    import IDirective = angular.IDirective;
    

    不符合顶级import声明,因此不会将其包含文件视为模块。即使它们位于文件的顶层,也是如此。原因是模块系统,无论是AMD,CommonJS,System还是ES2015, all 都使用字符串作为模块说明符,从这些字符串导入;顺便提一下,它可能代表文件路径,URL,已解析的简单名称或合成模块ID。

  3. 同样,代码中的import name = qualified.global.name语句是与模块无关的TypeScript特定功能。它们对于嵌套类型和值的别名非常有用,但它们不会产生模块。

    现在,这里有趣的地方,以及它与您的特定问题namespace的交叉点可以,有时非常优雅,来自 外部模块,但它们的语义非常不同

    考虑

    <强> services.ts

    export namespace app {
      export class SomeService { }
    }
    

    编译成以下JavaScript

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

    <强> main.ts

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

    编译成以下JavaScript

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

    上述两个都是外部模块,即真正的模块,它们使用命名空间作为内部代码组织机制,但它们的关键点是它们贡献给共享 app命名空间,每个都有自己的文件范围app变量。既没有对其他成员的隐式访问权限,namespace app的声明也不会跨文件合并,并且它们具有类似的内部命名方案是模块化的附带条件。

    那么所有这些与您的问题以及您尝试申请的建议有什么关系?

    让我们看看

    <强> headers.ts

    module vi.input.header {
      import IDirective = angular.IDirective;
    
      export class VIInputDirective implements IDirective {
        static whatever() { }
      }
    }
    

    此文件不是模块,如上所述,并使用全局命名空间来公开其声明。如果我们今天写这篇文章,我们会按惯例使用namespace关键字而不是module关键字。

    <强> main.ts

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

    事实上,由于您正在导入 headers.ts ,因为它不是模块,因此我们刚才看到它不是模块。此外,第一行是从模块说明符字符串导入的顶级import语句,具有讽刺意味的是, main.ts 本身就是一个模块。

    简而言之,两种风格混合不好,在它们之间共享代码并不简单,也不是你应该尝试做的事情(我已经将UMD格式留在了这个答案中,试图让它保持相对简单)

    现在我们来了一圈

    强文

    declare module vi.input {
        ....
    }
    
      

    如果我从&#34; ./ header.ts&#34 ;;交换import {VIInputDirective}在带有导入的main.ts文件中VIMHeaderDirective = vi.input.header.VIInputDirective;它工作正常,但然后在transpile / inject上的webpack给我以下错误:

    确实如上所述。此import未定位模块说明符字符串,并且没有任何其他顶级导入或导出,会更改 main.ts ,使其不再是模块。这会导致TypeScript正确地进行TypeCheck,使用import = namespace.value相互引用的全局变量是完全合法的,但这些不是模块,而且像Webpack这样的JavaScript工具在模块上运行。

    那么你将如何在这个勇敢的新世界中编写这个应用程序? 由于您使用的是模块捆绑工具Webpack,因此您可以使用适当的模块一直编写它。

    <强> main.ts

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

    <强> headers.ts

    import {IDirective} from 'angular';
    
    export class VIInputDirective implements IDirective {
      static whatever() { }
    }
    

    material.d.ts 不再像你写这篇文章时那样,因为它已被更新以使用适当的模块。如果需要从中引用某些内容,请使用模块语法

    我-对话框的options.ts

    import {material} from 'angular';
    
    const dialogOptions: material.IDialogOptions = { ... };
    
    export default dialogOptions;
    

    我尽量不要过于简单化,但为了避免在这个问题上写一篇中篇小说,有些人挥手是必要的,但我相信我希望能够涵盖并传达关键点。

答案 1 :(得分:0)

您正在寻找名称空间而不是模块,所以。

<强> header.ts

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

  }
}

<强> main.ts

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