declare module vs declare namespace typescript

时间:2016-04-15 06:45:34

标签: angularjs typescript1.5

我对打字稿内部模块中的func TestSomething(t *testing.T) { (*myT)(t).assert(0) } declare module感到困惑。

我不清楚的另一点是

对打字稿内部模块使用复杂的命名空间是一个好主意。 e.g。

declare namespace

2 个答案:

答案 0 :(得分:5)

我找到了有关命名空间和模块的[1]手册。首先,这些有关术语的澄清:

  

有关术语的说明:必须注意,在TypeScript 1.5中,术语已更改。 “内部模块”现在是“命名空间”。 “外部模块”现在只是“模块”,-

因此,从同一本书中,不同的章节[2]两者的示例:

模块

  interface StringValidator {
      isAcceptable(s: string): boolean;
  }
  ..
   // Validators to use
  let validators: { [s: string]: StringValidator; } = {};

命名空间

  namespace Validation {
      export interface StringValidator {
          isAcceptable(s: string): boolean;
      }
   ...
    // Validators to use
    let validators: { [s: string]: Validation.StringValidator; } = {};

来源[3]讲述了它们的用法:

  

不在外部模块中的命名空间可以跨越文件,因此,如果您在同一命名空间中,则可以引用该命名空间导出的任何内容,而无需进行任何类型的导入。

在这种情况下,您可能会使用命名空间,但在[4]比较中有提及:

模块:

   //typescript
   export class Validaton { 
       ... 
   }

   //becomes, in javascript:
   export class Validation {
       ... 
   }

命名空间:

   // typescript:
   namespace Validation {
       const foo = 123;
   }

  //This becomes, in es6:
  (function(Validation) {
         Validation.foo = 123;
   })(Validation || (Validation = {}))

并且如您所见,第二个在JavaScript ES6中变得非常不自然,因此[3]上的另一个答案甚至声明了名称空间已过时。


关于第二个问题:

源[1]讲述了一种情况,过度使用名称空间会使代码看起来毫无用处的复杂:

   import * as shapes from "./shapes";
   let t = new shapes.Shapes.Triangle(); // shapes.Shapes?

因此,这让我想到:对于代码名称空间级别,应避免所有无用或意义不大的事情。作家甚至说:

  

TypeScript中模块的主要功能是两个不同的模块永远不会在同一作用域中提供名称。由于模块的使用者可以决定要为其分配的名称,因此无需主动将导出的符号包装在名称空间中。


来源:

[1] https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html

[2] https://www.typescriptlang.org/docs/handbook/namespaces.html#validators-in-a-single-file

[3] Module vs Namespace - Import vs Require Typescript

[4] https://michelenasti.com/2019/01/23/is-typescript-namespace-feature-deprecated.html

答案 1 :(得分:4)

declare关键字用于在打字稿中创建环境声明,从而为编译器提供有关每个模块的类型信息。

在ES6之前,JavaScript没有module的概念。您创建的每个变量都将在全局范围内结束。 例如。如果您具有以下2个文件:

// a.js
var name = 'a';
// b.js
var name = 'b';

name将是一个全局变量,因此,如果在a.js之后加载b.js,则name的值为'b';

引入模块模式是为了防止这些变量冲突:

// a.js
(function() {
    var name = 'a';
})();
// b.js
(function() {
    var name = 'b';
})();

现在,每个文件在功能范围内将具有其自己的name变量。无法在函数外部访问它们。

要导出变量时,可以通过传入全局变量来实现:

var module = {}; // this is global

// a.js
(function(mod) {
    var name = 'a';
    mod.a = name;
})(module); // <-- passing the module variable into the function scope of a.js

// b.js
(function(mod) {
    var name = 'b';
    mod.b = name;
})(module);

现在,这些值已导出到module.amodule.b。在ES6之前,这是许多JS库用于创建模块的机制。 打字稿称为这种机制internal modules,后来被重命名为namespaces

  

命名空间只是全局命名空间中的JavaScript对象

可以使用declare namespace语法定义这些全局模块变量的环境类型声明。



自从引入ES6以来,模块已成为语言本身的功能,并且可以使用导入和导出语法来创建。因此,这些模块的类型声明将使用declare module语法。

  

从ECMAScript 2015开始,模块是该语言的本机部分,并且应由所有兼容的引擎实现支持。因此,对于新项目,模块将是推荐的代码组织机制

https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html


关于第二个问题,通常是一种更好的设计,以尽可能减少模块对象的嵌套级别。