如何将命名空间与 TypeScript 外部模块一起使用? [英] How do I use namespaces with TypeScript external modules?

查看:35
本文介绍了如何将命名空间与 TypeScript 外部模块一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码:

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.跨文件组合同一命名空间中的多个对象是行不通的.我该怎么做?

This is all very confusing. I want to have a bunch of external modules all contribute types to the same namespace, Living.Things. It seems that this doesn't work at all -- I can't see Animal in dogs.ts. I have to write the full namespace name b.Living.Things.Plant in tree.ts. It doesn't work to combine multiple objects in the same namespace across file. How do I do this?

推荐答案

糖果杯比喻

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

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

Candy Cup Analogy

Version 1: A cup for every candy

Let's say you wrote some code like this:

Mod1.ts

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

Mod2.ts

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

Mod3.ts

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

您已创建此设置:

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

Each module (sheet of paper) gets its own cup named A. This is useless - you're not actually organizing your candy here, you're just adding an additional step (taking it out of the cup) between you and the treats.

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

If you weren't using modules, you might write code like this (note the lack of export declarations):

global1.ts

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

global2.ts

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

global3.ts

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

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

This code creates a merged namespace A in the global scope:

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

This setup is useful, but doesn't apply in the case of modules (because modules don't pollute the global scope).

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

Going back to the original example, the cups A, A, and A aren't doing you any favors. Instead, you could write the code as:

Mod1.ts

export class Twix { ... }

Mod2.ts

export class PeanutButterCup { ... }

Mod3.ts

export class KitKat { ... }

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

to create a picture that looks like this:

好多了!

现在,如果你还在考虑你真正想要在你的模块中使用命名空间的程度,请继续阅读......

Now, if you're still thinking about how much you really want to use namespace with your modules, read on...

我们首先需要回到命名空间存在的根源,并检查这些原因是否对外部模块有意义.

We need to go back to the origins of why namespaces exist in the first place and examine whether those reasons make sense for external modules.

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

Organization: Namespaces are handy for grouping together logically-related objects and types. For example, in C#, you're going to find all the collection types in System.Collections. By organizing our types into hierarchical namespaces, we provide a good "discovery" experience for users of those types.

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

Name Conflicts: Namespaces are important to avoid naming collisions. For example, you might have My.Application.Customer.AddForm and My.Application.Order.AddForm -- two types with the same name, but a different namespace. In a language where all identifiers exist in the same root scope and all assemblies load all types, it's critical to have everything be in a namespace.

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

Do those reasons make sense in external modules?

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

Organization: External modules are already present in a file system, necessarily. We have to resolve them by path and filename, so there's a logical organization scheme for us to use. We can have a /collections/generic/ folder with a list module in it.

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

Name Conflicts: This doesn't apply at all in external modules. Within a module, there's no plausible reason to have two objects with the same name. From the consumption side, the consumer of any given module gets to pick the name that they will use to refer to the module, so accidental naming conflicts are impossible.

即使您不相信模块的工作方式可以充分解决这些原因,解决方案"也可以提供.尝试在外部模块中使用命名空间甚至不起作用.

Even if you don't believe that those reasons are adequately addressed by how modules work, the "solution" of trying to use namespaces in external modules doesn't even work.

一个故事:

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

Your friend Bob calls you up. "I have a great new organization scheme in my house", he says, "come check it out!". Neat, let's go see what Bob has come up with.

你从厨房开始,打开食品储藏室.有 60 个不同的盒子,每个盒子都标有Pantry".你随机挑选一个盒子并打开它.里面是一个标有谷物"的盒子.你打开谷物"盒子并找到一个标有意大利面"的盒子.你打开意大利面"盒子并找到一个标有Penne"的盒子.你打开这个盒子,如你所料,发现了一袋通心粉.

You start in the kitchen and open up the pantry. There are 60 different boxes, each labelled "Pantry". You pick a box at random and open it. Inside is a single box labelled "Grains". You open up the "Grains" box and find a single box labelled "Pasta". You open the "Pasta" box and find a single box labelled "Penne". You open this box and find, as you expect, a bag of penne pasta.

有点困惑,你拿起了一个相邻的盒子,也标有Pantry".里面是一个盒子,同样标有谷物".你打开谷物"盒子,再次找到一个标有Pasta"的盒子.你打开意大利面"盒子并找到一个盒子,这个盒子被标记为Rigatoni".你打开这个盒子,发现......一袋意大利通心粉.

Slightly confused, you pick up an adjacent box, also labelled "Pantry". Inside is a single box, again labelled "Grains". You open up the "Grains" box and, again, find a single box labelled "Pasta". You open the "Pasta" box and find a single box, this one is labelled "Rigatoni". You open this box and find... a bag of rigatoni pasta.

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

"It's great!" says Bob. "Everything is in a namespace!".

但是鲍勃……"你回复."你的组织架构没用.你必须打开一堆盒子才能找到任何东西,实际上,找到任何东西并不比把所有东西都放在一个盒子里而不是三个更方便>.事实上,由于您的食品储藏室已经按货架分类,您根本不需要这些盒子.为什么不把意大利面放在架子上,需要的时候拿起来呢?"

"But Bob..." you reply. "Your organization scheme is useless. You have to open up a bunch of boxes to get to anything, and it's not actually any more convenient to find anything than if you had just put everything in one box instead of three. In fact, since your pantry is already sorted shelf-by-shelf, you don't need the boxes at all. Why not just set the pasta on the shelf and pick it up when you need it?"

"你不明白——我需要确保没有其他人将不属于Pantry"命名空间的东西放入.而且我已经安全地将我所有的面食组织到 Pantry.Grains.Pasta 命名空间中,以便我可以轻松找到它"

"You don't understand -- I need to make sure that no one else puts something that doesn't belong in the 'Pantry' namespace. And I've safely organized all my pasta into the Pantry.Grains.Pasta namespace so I can easily find it"

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

Bob is a very confused man.

模块是自己的盒子

您可能在现实生活中遇到过类似的情况:您在亚马逊上订购了一些东西,每件商品都显示在自己的盒子里,里面有一个较小的盒子,您的商品包裹在自己的包装中.即使内部盒子相似,货物也不能有效地组合".

Modules are Their Own Box

You've probably had something similar happen in real life: You order a few things on Amazon, and each item shows up in its own box, with a smaller box inside, with your item wrapped in its own packaging. Even if the interior boxes are similar, the shipments are not usefully "combined".

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

Going with the box analogy, the key observation is that external modules are their own box. It might be a very complex item with lots of functionality, but any given external module is its own box.

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

Now that we've figured out that we don't need to use 'namespaces', how should we organize our modules? Some guiding principles and examples follow.

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

MyClass.ts

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

MyFunc.ts

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

消费

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

这对消费者来说是最佳选择.他们可以随意命名您的类型(在本例中为 t),并且无需进行任何无关的打点即可找到您的对象.

This is optimal for consumers. They can name your type whatever they want (t in this case) and don't have to do any extraneous dotting to find your objects.

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

MyThings.ts

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

消费

import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();

  • 如果您要导出大量内容,那么您才应该使用 module/namespace 关键字:
    • If you're exporting a large number of things, only then should you use the module/namespace keyword:
    • 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();
      


      危险信号

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


      Red Flags

      All of the following are red flags for module structuring. Double-check that you're not trying to namespace your external modules if any of these apply to your files:

      • 一个文件,其唯一的顶级声明是 export module Foo { ... }(删除 Foo 并将所有内容上"一层)
      • 具有单个 export classexport function 的文件不是 export default
      • 多个文件在顶层具有相同的export module Foo {(不要认为这些文件会合并成一个Foo!)
      • A file whose only top-level declaration is export module Foo { ... } (remove Foo and move everything 'up' a level)
      • A file that has a single export class or export function that isn't export default
      • Multiple files that have the same export module Foo { at top-level (don't think that these are going to combine into one Foo!)

      这篇关于如何将命名空间与 TypeScript 外部模块一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆