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

2020/10/11 09:41 · javascript ·  · 0评论

我有一些代码:

baseTypes.ts

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

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

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

// 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在整个文件的同一名称空间中组合多个对象是行不通的。我该怎么做呢?

糖果杯比喻

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

您已创建此设置:
在此处输入图片说明

每个模块(纸片)都有一个名为的杯子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:无杯

让我们回到最初的例子,杯子AAA没有做你任何好处。相反,您可以将代码编写为:

Mod1.ts

export class Twix { ... }

Mod2.ts

export class PeanutButterCup { ... }

Mod3.ts

export class KitKat { ... }

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

在此处输入图片说明

好多了!

现在,如果您仍在考虑要在模块中真正使用名称空间的内容,请继续阅读...


这些不是您要寻找的概念

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

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

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

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

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

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


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

盒子里的盒子盒子里的盒子

一个故事:

您的朋友鲍勃给您打电话。他说:“我家里有一个很棒的新组织方案”,“快来检查一下!”。整洁,让我们看看鲍勃想出了什么。

您从厨房开始,打开厨房。有60个不同的框,每个框标记为“ Pantry”。您随机选择一个盒子并将其打开。里面是一个标有“谷物”的盒子。您打开“谷物”框,找到一个标有“意大利面”的框。您打开“面食”框,找到一个标有“ Penne”的框。您打开这个盒子,然后按预期找到一袋通心粉。

有点困惑,您拿起一个相邻的框,也标记为“ Pantry”。里面是一个单独的盒子,再次标记为“谷物”。您打开“谷物”框,然后再次找到一个标记为“意大利面”的框。您打开“面食”框并找到一个框,该框标记为“ Rigatoni”。您打开这个盒子,然后发现...一包通心粉通心粉。

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

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

“您不明白-我需要确保没有其他人将不属于'Pantry'名称空间的内容放入其中。而且我已经将所有面食安全地组织到了该Pantry.Grains.Pasta名称空间中,以便可以轻松找到它。”

鲍勃是个很困惑的人。

模块是他们自己的盒子

您可能在现实生活中也发生过类似的事情:您在Amazon上订购了几样东西,每个项目都显示在自己的盒子里,里面有一个较小的盒子,您的项目包装在自己的包装中。即使内部包装盒相似,也不能有效地将货物“合并”。

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


外部模块指南

既然我们已经知道我们不需要使用“命名空间”,那么我们应该如何组织模块?遵循一些指导原则和示例。

导出尽可能接近顶层

  • 如果仅导出单个类或函数,请使用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!)

Ryan的回答没什么问题,但是对于那些来这里寻找如何在仍然正确使用ES6名称空间的同时维护一个文件一类结构的人们,请参考Microsoft的有用资源。

阅读文档后,我不清楚的一件事是:如何使用单个 导入整个(合并的)模块import

编辑
回圈以更新此答案。
TS中出现了几种命名空间的方法。

所有模块类都在一个文件中。

export namespace Shapes {
    export class Triangle {}
    export class Square {}      
}

将文件导入命名空间,然后重新分配

import { Triangle as _Triangle } from './triangle';
import { Square as _Square } from './square';

export namespace Shapes {
  export const Triangle = _Triangle;
  export const Square = _Square;
}

桶装

// ./shapes/index.ts
export { Triangle } from './triangle';
export { Square } from './square';

// in importing file:
import * as Shapes from './shapes/index.ts';
// by node module convention, you can ignore '/index.ts':
import * as Shapes from './shapes';
let myTriangle = new Shapes.Triangle();

最后的考虑。可以为每个文件命名空间

// triangle.ts
export namespace Shapes {
    export class Triangle {}
}

// square.ts
export namespace Shapes {
    export class Square {}
}

但是,当从同一个名称空间导入两个类时,TS会抱怨那里有一个重复的标识符。这次唯一的解决方案是为名称空间添加别名。

import { Shapes } from './square';
import { Shapes as _Shapes } from './triangle';

// ugh
let myTriangle = new _Shapes.Shapes.Triangle();

这种别名是绝对可恶的,所以不要这样做。您最好使用上述方法。就个人而言,我更喜欢“桶”。

尝试按文件夹组织:

baseTypes.ts

export class Animal {
    move() { /* ... */ }
}

export class Plant {
    photosynthesize() { /* ... */ }
}

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

export class Dog extends b.Animal {
    woof() { }
}   

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

class Tree extends b.Plant {
}

LivingThings.ts

import dog = require('./dog')
import tree = require('./tree')

export = {
    dog: dog,
    tree: tree
}

主要

import LivingThings = require('./LivingThings');
console.log(LivingThings.Tree)
console.log(LivingThings.Dog)

这样的想法是,您的模块本身不需要关心/知道它们正在参与名称空间,但是这会以一种紧凑,明智的方式向您的用户展示API,而这与您要用于项目的模块系统类型无关。

Albinofrenchy的小改进答案:

基本

export class Animal {
move() { /* ... */ }
}

export class Plant {
  photosynthesize() { /* ... */ }
}

import * as b from './base';

export class Dog extends b.Animal {
   woof() { }
} 

东西

import { Dog } from './dog'

namespace things {
  export const dog = Dog;
}

export = things;

主要

import * as things from './things';

console.log(things.dog);

OP,我和你在一起。同样,获得300+票的答案也没有错,但是我的看法是:

  1. 将类分别放入自己舒适的温暖文件中有什么问题?我的意思是,这会让事情看起来好多了吧?(或者所有模型都像1000行文件一样的人)

  2. 因此,如果要实现第一个,我们必须导入import import import ...仅在每个模型文件(例如man,srsly,模型文件,.d.ts文件)中导入,为什么会有这么多*在那儿?它应该简单,整洁,仅此而已。为什么在那里需要进口?为什么?C#得到名称空间是有原因的。

  3. 届时,您将使用“ filenames.ts”作为标识符。作为标识符...现在就来2017年,我们仍然这样做吗?伊玛回到火星,再睡1000年。

因此,可悲的是,我的答案是:不,如果您不使用所有这些导入或不使用这些文件名作为标识符(我认为这很愚蠢),则无法使“名称空间”功能正常运行。另一个选择是:将所有这些依赖项放入一个名为filenameasidentifier.ts的框中,然后使用

export namespace(or module) boxInBox {} .

将它们包装起来,这样当他们只是试图从类的引用中直接坐在它们之上时,他们就不会尝试访问同名的其他类。

我在这个主题上看到的几个问题/评论对我来说似乎是该人正在使用的Namespace意思是“模块别名”的地方。正如Ryan Cavanaugh在其评论中提到的那样,您可以有一个“包装器”模块,可重新导出多个模块。

如果您真的想从相同的模块名称/别名导入所有文件,请将包装器模块与中的路径映射结合在一起tsconfig.json

例:

./path/to/CompanyName.Products/Foo.ts

export class Foo {
    ...
}

./path/to/CompanyName.Products/Bar.ts

export class Bar {
    ...
}

./path/to/CompanyName.Products/index.ts

export { Foo } from './Foo';
export { Bar } from './Bar';

tsconfig.json

{
    "compilerOptions": {
        ...
        paths: {
            ...
            "CompanyName.Products": ["./path/to/CompanyName.Products/index"],
            ...
        }
        ...
    }
    ...
}

main.ts

import { Foo, Bar } from 'CompanyName.Products'

注意:需要以某种方式处理输出.js文件中的模块分辨率,例如使用此https://github.com/tleunen/babel-plugin-module-resolver

.babelrc处理别名解析的示例

{
    "plugins": [
        [ "module-resolver", {
            "cwd": "babelrc",
            "alias": {
                "CompanyName.Products": "./path/to/typescript/build/output/CompanyName.Products/index.js"
            }
        }],
        ... other plugins ...
    ]
}

试试这个名称空间模块

namespaceModuleFile.ts

export namespace Bookname{
export class Snows{
    name:any;
    constructor(bookname){
        console.log(bookname);
    }
}
export class Adventure{
    name:any;
    constructor(bookname){
        console.log(bookname);
    }
}
}





export namespace TreeList{
export class MangoTree{
    name:any;
    constructor(treeName){
        console.log(treeName);
    }
}
export class GuvavaTree{
    name:any;
    constructor(treeName){
        console.log(treeName);
    }
}
}

bookTreeCombine.ts

---编译部分-

import {Bookname , TreeList} from './namespaceModule';
import b = require('./namespaceModule');
let BooknameLists = new Bookname.Adventure('Pirate treasure');
BooknameLists = new Bookname.Snows('ways to write a book'); 
const TreeLis = new TreeList.MangoTree('trees present in nature');
const TreeLists = new TreeList.GuvavaTree('trees are the celebraties');

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

export module Living.Things {
    // Error, can't find name 'Animal', ??
    // Solved: can find, if properly referenced; exporting modules is useless, anyhow
    export class Dog extends b.Living.Things.Animal {
        public woof(): void {
            return;
        }
    }
}

// Error, can't use the same name twice, ??
// Solved: cannot declare let or const variable twice in same scope either: just use a different name
import b = require('./baseTypes');
import d = require('./dog');

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

组织代码的正确方法是使用单独的目录代替名称空间。每个类将位于其自己的文件中,位于其各自的命名空间文件夹中。index.ts将仅重新导出每个文件;index.ts文件中不应包含任何实际代码。这样组织代码可以使导航变得更加容易,并且可以基于目录结构进行自我记录。

// index.ts
import * as greeter from './greeter';
import * as somethingElse from './somethingElse';

export {greeter, somethingElse};

// greeter/index.ts
export * from './greetings.js';
...

// greeter/greetings.ts
export const helloWorld = "Hello World";

然后,您可以按以下方式使用它:

import { greeter } from 'your-package'; //Import it like normal, be it from an NPM module or from a directory.
// You can also use the following syntax, if you prefer:
import * as package from 'your-package';

console.log(greeter.helloWorld);
本文地址:http://javascript.askforanswer.com/ruhejiangmingchengkongjianyutypescriptwaibumokuaiyiqishiyong.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!