typescript 模块 vs 命名空间 - 导入 vs 需要打字稿

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38582352/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 07:14:45  来源:igfitidea点击:

Module vs Namespace - Import vs Require Typescript

typescript

提问by Reddy

I am getting lot of confusion with module/namespace/exportand import, require, referenceusage. Being from Java background, Can someone explain me in nutshell when to use what and what's the right design? I feel I am messing up when I am writing sample project

我对module/namespace/exportimport, require, reference用法感到很困惑。来自 Java 背景,有人可以简单地解释一下什么时候使用什么以及什么是正确的设计?写示例项目的时候感觉自己搞砸了

So far this is my understanding 1. moduleis for external packages 2. namespaceis for internal packages

到目前为止,这是我的理解 1.module用于外部包 2.namespace用于内部包

  • I didn't get how we categorize them?
  • When to export a class or namespace or package?
  • If we export package/namespace, all classes within that are exported or need to be explicitly exported
  • How each one of them can be imported/required?
  • 我不明白我们是如何分类的?
  • 何时导出类、命名空间或包?
  • 如果我们导出包/命名空间,其中的所有类都被导出或需要显式导出
  • 如何导入/需要它们中的每一个?

According to doc, if I am creating each "ts" file for each manager/model, Typescript doesn't recommend using "namespaces"? Directly use reference paths?

根据 doc,如果我为每个经理/模型创建每个“ts”文件,Typescript 不建议使用“命名空间”?直接使用引用路径?

Please explain in detail as I am coming from different background and not sure about ES6/ES5 etc`.

请详细解释,因为我来自不同的背景并且不确定 ES6/ES5 等。

I have seen several people raising/getting confused with same questions. I hope someone can explain in detail with real world scenario

我见过几个人提出/对相同的问题感到困惑。我希望有人可以用真实世界的场景详细解释

采纳答案by kayjtea

I didn't get how we categorize them?

我不明白我们是如何分类的?

Namespaces are used to organize/encapsulate your code. External modules are used to organize/encapsulate your code AND to locate your code at runtime. In practice, you have two choices at runtime: 1) combine all transpiled code into one file, or 2) use external modules and have multiple files and require some other mechanism to get at those files.

命名空间用于组织/封装您的代码。外部模块用于组织/封装您的代码并在运行时定位您的代码。实际上,您在运行时有两种选择:1) 将所有转译的代码合并到一个文件中,或者 2) 使用外部模块并拥有多个文件,并且需要某种其他机制来获取这些文件。

When to export a class or namespace or package?

何时导出类、命名空间或包?

To make a type or value visible outside of the file that it's in, you have to export it if it's inside of a namespace. Whether you export it at the top level or within a namespace will decide if it's now in an external module.

要使类型或值在它所在的文件之外可见,如果它在命名空间内,则必须将其导出。是在顶层还是在命名空间中导出它将决定它现在是否在外部模块中。

If we export package/namespace, all classes within that are exported or need to be explicitly exported

如果我们导出包/命名空间,其中的所有类都被导出或需要显式导出

Classes in a namespace will always need to be explicitly exported for the class to be visible at compile time outside of the file in which it is defined.

命名空间中的类总是需要显式导出,以便类在编译时在定义它的文件之外可见。

How each one of them can be imported/required?

如何导入/需要它们中的每一个?

This depends of if you're using external modules. An external module will always need to be imported to "use" it. Importing a namespace that's not in an external module is really just providing an alias for the namespace -- you still have to prefix the type/whatever with the alias (and this is why you generally don't want to use namespaces with external modules; doing so means you always have to use a prefix when referencing anything provided by the external module.) Namespaces that aren't in an external module can span files, so if you're in the same namespace you can refer to anything exported by the namespace without needing any sort of import.

这取决于您是否使用外部模块。始终需要导入外部模块才能“使用”它。导入不在外部模块中的命名空间实际上只是为命名空间提供了一个别名——您仍然必须在类型/任何内容前加上别名(这就是为什么您通常不想将命名空间与外部模块一起使用;这样做意味着您在引用外部模块提供的任何内容时始终必须使用前缀。)不在外部模块中的命名空间可以跨越文件,因此如果您在同一个命名空间中,您可以引用由外部模块导出的任何内容命名空间而无需任何类型的导入。

To really understand the above you need some background knowledge. The key thing to understand with references/namespaces/external modules is what these constructs do at compile time and what they do at runtime.

要真正理解上述内容,您需要一些背景知识。理解引用/命名空间/外部模块的关键是这些构造在编译时的作用以及它们在运行时的作用。

Reference directives are used at compile time to locate type information. Your source has a particular symbol in it. How does the TypeScript compiler locate the definition for that symbol? The reference directive has largely been subsumed by the tsconfig.json mechanism -- using tsconfig.json, you tell the compiler where all your sources are.

在编译时使用引用指令来定位类型信息。您的来源中有一个特定的符号。TypeScript 编译器如何定位该符号的定义?参考指令在很大程度上被 tsconfig.json 机制包含——使用 tsconfig.json,你告诉编译器你所有的源在哪里。

Namespaces can contain type definitions and/or implementation. If a namespace contain only type information then it has no runtime manifestation at all -- you can check this by looking at the JS output and finding an empty JS file. If a namespace has implementation code, then the code is wrapped inside a closure that is assigned to a global variable with the same name as the namespace. With nested namespaces, there will be a global variable for the root name space. Again, check the JS output. Namespaces are historically how JS client-side libraries have attempted to avoid the issue with naming collisions. The idea is to wrap your entire library into one closure and then expose as small a global footprint as possible -- just one global variable referencing the closure. Well, the problem is still that you've claimed a name in the global space. What if you wanted, say, two versions of a library? A TypeScript namespace still has the issue of how to locate the source for the namespace. That is, source code that references A.B still has the problem of telling the compiler how to locate A.B -- either by using reference directives or by using tsconfig.json. Or by putting the namespace into an external module and then importing the external module.

命名空间可以包含类型定义和/或实现。如果一个命名空间只包含类型信息,那么它根本没有运行时表现——你可以通过查看 JS 输出并找到一个空的 JS 文件来检查这一点。如果命名空间有实现代码,那么代码被包装在一个闭包中,该闭包被分配给一个与命名空间同名的全局变量。对于嵌套命名空间,根命名空间将有一个全局变量。再次检查 JS 输出。命名空间历来是 JS 客户端库试图避免命名冲突问题的方式。这个想法是将您的整个库包装到一个闭包中,然后公开尽可能小的全局足迹——只有一个引用闭包的全局变量。好吧,问题仍然是你在全局空间中声明了一个名字。如果你想要一个库的两个版本怎么办?TypeScript 命名空间仍然存在如何定位命名空间源的问题。也就是说,引用 AB 的源代码仍然存在告诉编译器如何定位 AB 的问题——通过使用引用指令或使用 tsconfig.json。或者通过将命名空间放入外部模块,然后导入外部模块。

External modules originated with server-side JS. There is a one-to-one correspondence between an external module and a file on the file system. You can use the file system directory structure to organize external modules into a nested structure. Importing an external module will generally aways introduce a runtime dependency on that external module (the exception is when you import an external module but then don't use any of its exports in the value position -- that is, you only import the external module to get at its type information). An external module is implicitly in a closure, and this is key: the user of the module can assign the closure to whatever local variable they want. TypeScript/ES6 adds additional syntax around mapping the exports of the external modules to local names, but this is just a nicety. On the server side, locating an external module is relatively straight forward: just locate the file representing the external module on the local file system. If you want to use external modules on the client side, in a browser, it gets more complex as there's no equivalent to the file system that has the module available for loading. So now on the client side you need a way to bundle all those files into a form that can be used remotely in the browser -- this is where module bundlers like Webpack (Webpack does a heck of a lot more than bundle modules though) and Browserify come into play. Module bundlers allow runtime resolution of your external modules in the browser.

外部模块起源于服务器端 JS。外部模块和文件系统上的文件是一一对应的。您可以使用文件系统目录结构将外部模块组织成嵌套结构。导入外部模块通常会引入对该外部模块的运行时依赖(例外情况是当您导入外部模块但随后不在值位置使用其任何导出时——也就是说,您只导入外部模块以获取其类型信息)。外部模块隐含在闭包中,这是关键:模块的用户可以将闭包分配给他们想要的任何局部变量。TypeScript/ES6 添加了额外的语法,将外部模块的导出映射到本地名称,但这只是一个细节。在服务器端,定位外部模块相对简单:只需在本地文件系统上定位代表外部模块的文件即可。如果您想在客户端使用外部模块,在浏览器中,它会变得更加复杂,因为没有与具有可用于加载的模块的文件系统等效的文件系统。所以现在在客户端,你需要一种方法将所有这些文件捆绑成一个可以在浏览器中远程使用的表单——这就是像 Webpack 这样的模块捆绑器(尽管 Webpack 比捆绑模块做的要多得多)和Browserify 开始发挥作用。模块捆绑器允许在浏览器中对外部模块进行运行时解析。在浏览器中,它变得更加复杂,因为没有可用于加载模块的文件系统。所以现在在客户端,你需要一种方法将所有这些文件捆绑成一个可以在浏览器中远程使用的表单——这就是像 Webpack 这样的模块捆绑器(尽管 Webpack 比捆绑模块做的要多得多)和Browserify 开始发挥作用。模块捆绑器允许在浏览器中对外部模块进行运行时解析。在浏览器中,它变得更加复杂,因为没有可用于加载模块的文件系统。所以现在在客户端,你需要一种方法将所有这些文件捆绑成一个可以在浏览器中远程使用的表单——这就是像 Webpack 这样的模块捆绑器(尽管 Webpack 做的比捆绑模块要多得多)和Browserify 开始发挥作用。模块捆绑器允许在浏览器中对外部模块进行运行时解析。

Real world scenario: AngularJS. Pretend external modules don't exist, use a single namespace to limit pollution of global space (in the example below a single variable MyApp is all that is in the global space), export only interfaces, and use AngularJS dependency-injection to make implementations available for use. Put all classes in a directory root, add a tsconfig.json to the root, install angularjs typings under the same directory root so that tsconfig.json picks it up too, combine all output int one JS file. This will work fine for most projects if code-reuse isn't much of a concern.

真实世界场景:AngularJS。假装外部模块不存在,使用单一命名空间来限制全局空间的污染(在下面的示例中,单个变量 MyApp 是全局空间中的全部),仅导出接口,并使用 AngularJS 依赖注入进行实现可供使用。把所有的类放在一个目录根目录下,在根目录下添加一个tsconfig.json,在同一个目录根目录下安装angularjstypings,这样tsconfig.json也会把它捡起来,将所有输出合并到一个JS文件中。如果代码重用不是什么大问题,这对于大多数项目来说都可以正常工作。

MyService.ts:

我的服务.ts:

namespace MyApp {

    // without an export the interface is not visible outside of MyService.ts
    export interface MyService { 
        ....
    }

    // class is not exported; AngularJS DI will wire up the implementation
    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
}

MyController.ts:

我的控制器.ts:

namespace MyApp {

   class MyController {
       // No import of MyService is needed as we are spanning 
       // one namespace with multiple files.
       // MyService is only used at compile time for type checking. 
       // AngularJS DI is done on the name of the variable. 
       constructor(private myService: MyService) { 
       }
   }
   angular.module("MyApp").controller("myController", MyController);
}

Using IIFE to avoid polluting global runtime scope. In this example, no global variables are created at all. (A tsconfig.json is assumed.)

使用 IIFE 避免污染全局运行时范围。在这个例子中,根本没有创建全局变量。(假设为 tsconfig.json。)

Foo.ts:

脚:

namespace Foo {
    // without an export IFoo is not visible. No JS is generated here
    // as we are only defining a type.
    export interface IFoo {
        x: string;
    }
}

interface ITopLevel {
    z: string;
}

(function(){
    // export required above to make IFoo visible as we are not in the Foo namespace
    class Foo1 implements Foo.IFoo {
        x: string = "abc";
    }
    // do something with Foo1 like register it with a DI system
})();

Bar.ts:

酒吧.ts:

// alias import; no external module created
import IFoo = Foo.IFoo;

(function() {

    // Namespace Foo is always visible as it was defined at
    // top level (outside of any other namespace).
    class Bar1 implements Foo.IFoo {
        x: string;
    }

    // equivalent to above
    class Bar2 implements IFoo {
        x: string;
    }

    // IToplevel is visible here for the same reason namespace Foo is visible
    class MyToplevel implements ITopLevel {
        z: string;
    }

})();

Using IIFE you can make eliminate introducing MyApp as a global variable in the first example.

使用 IIFE,您可以避免在第一个示例中将 MyApp 作为全局变量引入。

MyService.ts:

我的服务.ts:

interface MyService { 
    ....
}

(function() {

    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
})();

MyController.ts:

我的控制器.ts:

(function() { 

   class MyController { 
       constructor(private myService: MyService) { 
       }
   }

   angular.module("MyApp").controller("myController", MyController);
})();

回答by Paleo

There are two things:

有两件事:

  • A modulein TypeScript is a standard ES6 notion, it uses import/ exportkeywords at the top levelof the code;
  • A namespaceis a notion specific to TypeScript to help to organize the code in an obsolete fashion.
  • 模块在打字稿是一个标准ES6概念,它使用import/export关键字在顶层的代码;
  • 一个命名空间是一个特定的概念,以打字稿,以帮助组织在一个过时的时尚代码。

Namespaces

命名空间

It is almost an obsolete notion. Prior to ES6 modules, the common way to separate JavaScript code in a browser was to create global variables. For example, all the functions of an API like underscorewas located in a global variable named _.

这几乎是一个过时的概念。在 ES6 模块之前,在浏览器中分离 JavaScript 代码的常用方法是创建全局变量。例如,API 的所有功能(如下划线)都位于名为 的全局变量中_

This old way to proceed is like Java packages or PHP namespaces. It is not adapted to the Web. The new ECMAScript standard solves issues like: how to use two libraries that have the same name? How to use two distinct versions of the same library?

这种旧的处理方式类似于 Java 包或 PHP 命名空间。它不适用于 Web。新的 ECMAScript 标准解决了以下问题:如何使用两个同名的库?如何使用同一个库的两个不同版本?

Notice: In versions of TypeScript prior to the ECMAScript definition of “modules” (summer 2014), namespaces were called “internal modules” and modules were called “external modules”.

注意:在 ECMAScript 定义“模块”(2014 年夏季)之前的 TypeScript 版本中,命名空间被称为“内部模块”,模块被称为“外部模块”。

Notice 2: The keyword exportinside a namespaceis a non-standard TypeScript usage of the keyword. It is the means to declare a thing that is publicly accessible from outside the namespace.

通知2:关键字export内的namespace是该关键字的非标准打字稿用法。它是一种声明可从命名空间外部公开访问的事物的方法。

ES6 modules

ES6 模块

A module is a file that contains keywords importor exportat the top level of the code.

模块是包含关键字importexport位于代码顶层的文件。

TypeScript follows the standard from ECMAScript. I suggest to read a good introduction to ES6 modulesin an article from Mozilla.

TypeScript 遵循 ECMAScript 的标准。我建议阅读Mozilla 的一篇文章中对 ES6 模块的很好的介绍

If you want to use modules in a front-end application (in a browser), then you will have to use a bundler (Webpack[the documentation here], Browserify) or a loader (SystemJS[a tutorial here], RequireJS) and to configure TypeScript with this environment.

如果您想在前端应用程序中(在浏览器中)使用模块,那么您将不得不使用捆绑器(Webpack[此处的文档]、Browserify)或加载器(SystemJS[此处的教程]、RequireJS)和使用此环境配置 TypeScript。

If your code is executed in Node.js, just configure the TypeScript compiler to generate the CommonJS format.

如果您的代码是在 Node.js 中执行的,只需配置 TypeScript 编译器即可生成 CommonJS 格式。

Notice: A namespace can be declared in a module. In that case, it won't be accessible as a global variable from outside the module. However, it can be exported from the module.

注意:命名空间可以在模块中声明。在这种情况下,它不能作为全局变量从模块外部访问。但是,它可以从模块中导出。

回答by basarat

  1. module is for external packages 2. namespace is for internal packages
  1. 模块用于外部包 2. 命名空间用于内部包

Actually the modulekeyword has been replaced with the namespacekeyword.

实际上module关键字已被替换为namespace关键字。

A better statement is thus Modulesare what used to be called external modules, namespaceis what used to be called internal modules.

因此,更好的说法是Modules过去被称为外部模块,namespace过去被称为内部模块。

More

更多的

Hope this helps futher : https://basarat.gitbooks.io/typescript/content/docs/project/modules.html

希望这有助于进一步:https: //basarat.gitbooks.io/typescript/content/docs/project/modules.html

回答by Nir O.

"require" and "import" are equivalent in functionality. we can use them both interchangeably because we have transpilers that don't really care whether the browser supports them natively or not. but, while "require" has its roots in old coding style coming from CommonJS back to 2009, "import" is deriving its syntax from the widely accepted ES6 (ES2015) syntax. so you should use "import" for new projects and not "require".

require”和“ import”在功能上是等效的。我们可以互换使用它们,因为我们的转译器并不真正关心浏览器是否原生支持它们。但是,虽然“ require”起源于来自 CommonJS 的旧编码风格,可以追溯到 2009 年,“import”的语法源自广泛接受的 ES6 (ES2015) 语法。所以你应该对新项目使用“ import”而不是“ require”。