gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 05:09:56 26 4
gpt4 key购买 nike

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

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

  • 我不明白我们是如何分类的?
  • 何时导出类、命名空间或包?
  • 如果我们导出包/命名空间,其中的所有类都被导出或需要显式导出
  • 如何导入/需要它们中的每一个?

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

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

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

    最佳答案

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

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

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

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

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

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

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

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

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

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

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

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

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

    我的服务.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);
    }

    我的 Controller .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);
    }

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

    脚:
    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
    })();

    酒吧.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;
    }

    })();

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

    我的服务.ts:
    interface MyService { 
    ....
    }

    (function() {

    class MyServiceImpl implements MyService {
    }

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

    我的 Controller .ts:
    (function() { 

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

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

    关于typescript - 模块 vs 命名空间 - 导入 vs 需要 typescript ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38582352/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com