小编典典

如何将命名空间与 TypeScript 外部模块一起使用?

all

我有一些代码:

基本类型.ts

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

狗.ts

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

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

树.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.
似乎这根本不起作用——我Animaldogs.ts. 我必须b.Living.Things.Planttree.ts.
跨文件组合同一命名空间中的多个对象是行不通的。我该怎么做呢?


阅读 69

收藏
2022-05-05

共1个答案

小编典典

糖果杯类比

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

假设你写了一些这样的代码:

模组1.ts

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

模组2.ts

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

模组3.ts

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

您已创建此设置:
在此处输入图像描述

每个模块(一张纸)都有 自己的杯子 ,名为A. 这是没用的——你实际上并没有在这里 整理
你的糖果,你只是在你和零食之间增加了一个额外的步骤(把它从杯子里拿出来)。


版本 2:全球范围内的一杯

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

global1.ts

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

global2.ts

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

global3.ts

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

A代码在全局范围内创建一个合并的命名空间:

在此处输入图像描述

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


版本 3:去无杯

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

模组1.ts

export class Twix { ... }

模组2.ts

export class PeanutButterCup { ... }

模组3.ts

export class KitKat { ... }

创建一个看起来像这样的图片:

在此处输入图像描述

好多了!

现在,如果您仍在考虑您真正想在模块中使用多少命名空间,请继续阅读…


这些不是您要寻找的概念

我们需要回到最初为什么存在命名空间的起源,并检查这些原因是否对外部模块有意义。

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

名称冲突
:命名空间对于避免命名冲突很重要。例如,您可能有My.Application.Customer.AddFormMy.Application.Order.AddForm--
两个名称相同但命名空间不同的类型。在所有标识符都存在于同一根范围内并且所有程序集都加载所有类型的语言中,将所有内容都放在命名空间中至关重要。

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

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

名称冲突 :这根本不适用于外部模块。 一个模块中,没有合理的理由让两个对象具有相同的名称。从消费方面来看,任何给定模块的 消费者
都可以选择他们将用来指代模块的名称,因此不可能发生意外的命名冲突。


即使您不相信模块的工作方式充分解决了这些原因,尝试在外部模块中使用命名空间的“解决方案”甚至不起作用。

盒中盒盒中盒

一个故事:

你的朋友鲍勃给你打电话。“我家里有一个很棒的新组织计划”,他说,“来看看吧!”。好吧,让我们去看看鲍勃想出了什么。

你从厨房开始,打开储藏室。有 60
个不同的盒子,每个盒子都标有“Pantry”。你随机选择一个盒子并打开它。里面是一个标有“谷物”的盒子。您打开“谷物”框,找到一个标有“意大利面”的框。打开“Pasta”框,找到一个标有“Penne”的框。你打开这个盒子,正如你所料,发现了一袋通心粉。

有点困惑,你拿起一个相邻的盒子,也标有“Pantry”。里面是一个盒子,同样标有“谷物”。打开“Grains”框,再次找到一个标有“Pasta”的框。你打开“Pasta”盒子,找到一个盒子,这个盒子标有“Rigatoni”。你打开这个盒子,发现…
一袋通心粉。

“这很棒!” 鲍勃说。“一切都在命名空间中!”。

“但是鲍勃……”你回答。“你的组织方案没用。你必须打开一堆盒子才能找到任何东西,实际上找东西并不比你把所有东西都放在 一个 盒子里而不是 三个
盒子里更方便。事实上,因为你的储藏室已经按架子分类了,你根本不需要盒子。为什么不把意大利面放在架子上,当你需要的时候拿起它呢?

“你不明白——我需要确保没有其他人将不属于‘Pantry’命名空间的东西放入。而且我已经将我所有的意大利面安全地组织到Pantry.Grains.Pasta命名空间中,这样我就可以很容易地找到它。”

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

模块是他们自己的盒子

您可能在现实生活中发生过类似的事情:您在亚马逊上订购了一些东西,每件商品都出现在自己的盒子里,里面有一个较小的盒子,您的商品用自己的包装包裹着。即使内箱相似,货物也不能有效地“组合”。

与盒子类比,关键观察是 外部模块是它们自己的盒子 。它可能是一个具有很多功能的非常复杂的项目,但任何给定的外部模块都是它自己的盒子。


外部模块指南

既然我们已经弄清楚我们不需要使用“命名空间”,那么我们应该如何组织我们的模块呢?以下是一些指导原则和示例。

尽可能接近顶层导出

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

MyClass.ts

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

我的函数

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

消耗

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

这对消费者来说是最理想的。他们可以随心所欲地命名您的类型(t在这种情况下),并且不必做任何无关的点来查找您的对象。

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

MyThings.ts

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

消耗

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 { ... }
}

消耗

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

红旗

以下所有都是模块结构的危险信号。如果其中任何一个适用于您的文件,请仔细检查您是否没有尝试命名外部模块:

  • 一个文件,其唯一的顶级声明是export module Foo { ... }(删除Foo并将所有内容“向上”移动一个级别)
  • 具有单个export classexport function没有的文件export default
  • export module Foo {在顶层具有相同的多个文件(不要认为它们会合并为一个Foo!)
2022-05-05