我有一些代码:

baseTypes.ts

export namespace Living.Things {
  export class Animal {
    move() { /* ... */ }
  }
  export class Plant {
    photosynthesize() { /* ... */ }
  }
}

dog.ts

import b = require('./baseTypes');

export namespace Living.Things {
  // Error, can't find name 'Animal', ??
  export class Dog extends Animal {
    woof() { }
  }
}

tree.ts

// Error, can't use the same name twice, ??
import b = require('./baseTypes');
import b = require('./dogs');

namespace Living.Things {
  // Why do I have to write b.Living.Things.Plant instead of b.Plant??
  class Tree extends b.Living.Things.Plant {

  }
}

这一切都很令人困惑.我想让一堆外部模块都为同一个名称空间贡献类型,Living.Things.这似乎根本不起作用——我看不到dogs.ts中的Animal.我必须在tree.ts中写出完整的名称空间名称b.Living.Things.Plant.跨文件在同一名称空间中组合多个对象是行不通的.我该怎么做?

推荐答案

Candy Cup Analogy

版本1:每个糖果都有一个杯子

假设您编写了如下代码:

Mod1.ts个个

export namespace A {
    export class Twix { ... }
}

Mod2.ts

export namespace A {
    export class PeanutButterCup { ... }
}

Mod3.ts

export namespace A {
     export class KitKat { ... }
}

You've created this setup: enter image description here

每个模块(一张纸)都有its own cup个名为A的模块.这是没有用的——你实际上并不是在这里吃糖果,你只是在你和糖果之间增加了一个额外的步骤(从杯子中取出).


版本2:全局的一个杯子

如果不使用模块,可能会编写这样的代码(注意缺少export个声明):

global1.ts

namespace A {
    export class Twix { ... }
}

global2.ts

namespace A {
    export class PeanutButterCup { ... }
}

global3.ts

namespace A {
     export class KitKat { ... }
}

This代码在全局作用域中创建合并的命名空间A:

enter image description here

这种设置很有用,但不适用于模块(因为模块不会污染全局范围).


版本3:无杯化

回到最初的例子,杯子AAA对您没有任何帮助.相反,您可以将代码编写为:

Mod1.ts个个

export class Twix { ... }

Mod2.ts

export class PeanutButterCup { ... }

Mod3.ts

export class KitKat { ... }

要创建如下所示的图片:

enter image description here

好多了!

现在,如果您仍在思考您到底有多希望在模块中使用名称空间,请继续阅读...


These Aren't the Concepts You're Looking For

我们首先需要回到名称空间存在的原因,并判断这些原因对于外部模块是否有意义.

Organization:名称空间便于将逻辑相关的对象和类型组合在一起.例如,在C#中,您将在System.Collections中找到所有集合类型.通过将类型组织到分层名称空间中,我们为这些类型的用户提供了良好的"发现"体验.

Name Conflicts:名称空间对于避免命名冲突很重要.例如,您可能有My.Application.Customer.AddFormMy.Application.Order.AddForm——这两种类型具有相同的名称,但名称空间不同.在一种语言中,所有标识符都存在于同一根作用域中,所有程序集都加载所有类型,因此将所有内容都放在名称空间中是至关重要的.

这些原因在外部模块中有意义吗?

Organization:外部模块必须已经存在于文件系统中.我们必须通过路径和文件名来解析它们,所以我们可以使用一个逻辑组织方案.我们可以有一个包含list模块的/collections/generic/文件夹.

Name Conflicts:这完全不适用于外部模块.Within一个模块,没有合理的理由让两个对象具有相同的名称.从消费端来看,任何给定模块的用户都可以 Select 他们将用来引用该模块的名称,因此不可能出现意外的命名冲突.


即使您不认为模块的工作方式充分解决了这些原因,但try 在外部模块中使用名称空间的"解决方案"甚至都不起作用.

盒子里的盒子里的盒子

一个故事:

你的朋友鲍勃给你打了电话."我家里有一个很棒的新组织方案,"他说,"快来看看!"奈特,我们go 看看鲍勃想出了什么.

你从厨房开始,打开储藏室.有60个不同的盒子,每个盒子都贴着"食品储藏室"的标签.你随意挑选一个盒子,然后打开它.里面有一个单独的盒子,上面贴着"谷物"的标签.你打开"谷物"盒子,发现一个标有"意大利面"的盒子.你打开"意大利面"的盒子,发现只有一个标有"Penne"的盒子.你打开这个盒子,不出所料,发现了一袋便士意大利面.

有点困惑,你拿起一个相邻的盒子,上面也写着"餐具室".里面是一个单独的盒子,再次贴上"谷物"的标签.你打开"谷物"盒子,再次找到一个标有"意大利面"的盒子.你打开"意大利面"盒子,找到一个盒子,这个盒子的标签是"里加通心粉".你打开这个盒子,发现...一袋里加通心粉.

"太棒了!"鲍勃说."一切都在一个命名空间中!".

"但是鲍勃."你回答."你的组织方案是没用的.你必须打开一堆盒子才能拿到任何东西,而且实际上找什么都不比你把所有东西都放在one个盒子里而不是three个盒子里更方便.事实上,既然你的食品储藏室已经逐个货架分类了,你根本不需要这些盒子.为什么不把意大利面放在架子上,需要的时候再捡起来呢?"

"你不明白--我需要确保没有人把不属于‘食品储藏室’命名空间的东西放进‘食品储藏室’.而且我已经安全地把我所有的意大利面都组织到了Pantry.Grains.Pasta命名空间中,这样我就可以很容易地找到它."

鲍勃是一个非常困惑的人.

模块是它们自己的盒子

你可能在现实生活中也发生过类似的事情:你在亚马逊上订购了几样东西,每件商品都会出现在自己的盒子里,里面有一个更小的盒子,你的商品包装在自己的包装里.即使内部箱子相似,装运的Cargo 也没有有效的"组合".

按照盒子的比喻,关键的观察是external modules are their own box.它可能是一个非常复杂的项目,有很多功能,但是任何给定的外部模块都是它自己的盒子.


Guidance for External Modules

既然我们已经知道我们不需要使用"名称空间",那么我们应该如何组织我们的模块呢?下面是一些指导原则和例子.

出口尽可能接近顶级水平

  • 如果只导出单个类或函数,请使用export default:

MyClass.ts

export default class SomeType {
  constructor() { ... }
}

MyFunc.ts

function getThing() { return 'thing'; }
export default getThing;

Consumption

import t from './MyClass';
import f from './MyFunc';
var x = new t();
console.log(f());

这对消费者来说是最佳 Select .他们可以随心所欲地命名你的类型(本例中为t),并且不必做任何无关的点操作来找到你的对象.

  • 如果要导出多个对象,请将它们全部放在顶级:

MyThings.ts

export class SomeType { ... }
export function someFunc() { ... }

Consumption

import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();
  • 如果你要导出大量的东西,只有这样你才应该使用module/namespace关键字:

MyLargeModule.ts

export namespace Animals {
  export class Dog { ... }
  export class Cat { ... }
}
export namespace Plants {
  export class Tree { ... }
}

Consumption

import { Animals, Plants} from './MyLargeModule';
var x = new Animals.Dog();

Red Flags

以下所有内容都是模块 struct 的危险信号.如果其中任何一项适用于您的文件,请仔细判断您没有try 为外部模块设置命名空间:

  • 唯一顶级声明为export module Foo { ... }的文件(删除Foo并将所有内容"上移"一级)
  • 包含单个export classexport function而不是export default的文件
  • 多个文件在顶层有相同的export module Foo {个(不要认为这些文件会合并成一个Foo!)

Javascript相关问答推荐

为什么JavaScript双边字符串文字插值不是二次的?

有Angular 的material .未应用收件箱中的价值变化

通过在页面上滚动来移动滚动条

提交表格后保留Web表格中的收件箱值?

Javascript,部分重排序数组

类型自定义lazy Promise. all

网页自检测外部元素无法加载

为什么这个JS模块在TypeScript中使用默认属性导入?""

Angular 17—每当一个布尔变量变为真时触发循环轮询,只要它保持为真

html + java script!需要帮助来了解为什么我得到(无效的用户名或密码)

类构造函数不能在没有用With Router包装的情况下调用

Web Crypto API解密失败,RSA-OAEP

在FAQ Accodion应用程序中使用React useState时出现问题

如何限制显示在分页中的可见页面的数量

使用可配置项目创建网格

JSX/React -如何在组件props 中循环遍历数组

每隔一行文本段落进行镜像(Boustrophedon)

如何使用useparams从react路由中提取id

与find()方法一起使用时,Mongoose中的$or运算符没有提供所有必需的数据

鼠标进入,每秒将图像大小减小5%