在TypeScript文档中,namespaces被称为内部TypeScript模块,要在单个文件中使用它们作为引用,我们必须向名称空间添加ref标头,例如:/// <reference path="Validation.ts" />
,如文档。但是,我们还可以使用export declare namespace MyNameSpace
导出和声明名称空间,然后将其导入任何其他实体。是否有任何理由建议我们使用标头ref标签而不是导出名称空间声明。
在听到有关编译和运行时顺序的第一个答案之后,我认为也许最好提供一个示例,这是一个简单的问候应用程序,其中包含带有声明的名称空间的三个mains文件(我怀疑我们甚至需要这个文件)要在我们的应用范围内使用时导入)
文件名: GreetingApp.ts
export declare namespace GreetingApp {}
文件名: Hello.ts
namespace GreetingApp {
export class Hello {
constructor(private name: string) {
this.name = name.toLowerCase().charAt(0).toUpperCase() + name.substr(1).toLowerCase();
}
public toString(): string {
return "Hello! " + this.name;
}
}
}
文件名: Bye.ts
namespace GreetingApp {
export class Bye {
constructor(private name: string) {
this.name = name.toLowerCase().charAt(0).toUpperCase() + name.substr(1).toLowerCase();
}
public toString(): string {
return "Goodbye! " + this.name;
}
}
}
文件名: UseCase.ts (用于导入和实例化对象的测试文件)
import GreetingApp from './GreetingApp';
console.log(new GreetingApp.Hello('adam').toString());
console.log(new GreetingApp.Bye('adam').toString());
答案 0 :(得分:1)
如果不使用导出/导入,则所有文件都在全局范围内运行,并且如果多个文件声明具有相同名称的名称空间(如您链接的示例中),则这些名称空间将合并。这种方法可能非常方便。 TypeScript编译器源代码本身以及我的项目之一都使用了它。 (TypeScript团队声称外部模块优于名称空间,但是当它们将编译器从名称空间迁移到外部模块时,我相信它们!)请注意/// <reference path="...">
有两个用途:从目标文件加载声明,位于编译时,并确保在运行时先加载目标文件,然后再加载当前文件。如果满足以下两个条件,则可以省去/// <reference path="...">
:使用tsconfig.json
文件来确保所有文件都在编译时加载,并且运行时加载顺序不会没关系运行时加载顺序可能很重要的最常见原因之一是,如果定义一个类,然后将其扩展到另一个文件中。
如果导出和导入名称空间,则说明您正在使用外部模块,并且如果在不同的外部模块中声明两个具有相同名称的名称空间,它们将不会合并。一个外部模块可以包含一个模块扩展,该扩展将声明添加到另一个外部模块中的命名空间,但不能添加运行时代码。因此,这不像在全局范围内运行的文件中合并名称空间那样方便。
在UseCase.ts
中,我假设您打算使用import { GreetingApp } from './GreetingApp'
,因为import GreetingApp from './GreetingApp'
给了我一个编译错误。 (如果是,请更新问题。)
哇,您发现我以前不曾意识到的TypeScript名称解析的疯狂方面。在此示例中,Hello.ts
和Bye.ts
是全局文件,而不是外部模块(因为它们不包含顶级ES6导入或导出),因此两者都构成了全局文件中的GreetingApp
命名空间范围,与从GreetingApp
导出的GreetingApp.ts
名称空间无关。
那么UseCase.ts
中发生了什么?如here所述,TypeScript符号可以具有以下一种或多种含义:值(包括值的名称空间),类型和“名称空间”(即类型的名称空间)。 GreetingApp
中的GreetingApp.ts
命名空间仅具有名称空间含义,因为它不包含值,而全局GreetingApp
命名空间具有值含义和名称空间含义,因为它包含类,这些类包括值(构造函数)和类型(实例类型)。
在引用符号时,引用的上下文确定使用三种含义中的哪一种:如果将X.Y
用作值,则使用X
的值含义及其属性{使用{1}},而如果使用Y
作为类型,则会使用X.Y
的名称空间含义和X
中的Y
的类型含义。此外,符号的阴影按含义起作用!当X
从UseCase.ts
导入GreetingApp
时,它将导入./GreetingApp
中的GreetingApp
具有的唯一含义,这是名称空间的含义,掩盖了全局GreetingApp.ts
。全局GreetingApp
的值含义仍然可见,并由对GreetingApp
和GreetingApp.Hello
的引用使用。但是,您会注意到,如果尝试声明类型为GreetingApp.Bye
的变量,则该变量将不起作用,因为该引用使用了GreetingApp.Hello
的名称空间含义,该名称空间从{解析为空名称空间。 {1}},而不是全局名称空间。
我知道这种分析是非常技术性的,但是我希望我已经清楚地表明该示例无法按照您认为的方式工作(并且希望下一个因TypeScript的文档不足的阴影行为而困惑的人能够找到此线程)在网络搜索中)。在此示例中,导入和整个GreetingApp
文件都没有用。如果将它们都删除,则剩下的是“具有合并的命名空间的全局文件”方法来构建我在原始答案中描述的代码库。