如何将名称空间与 TypeScript 外部模块一起使用?
How do I use namespaces with TypeScript external modules?
我有一些代码:
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
。跨文件在同一个命名空间中组合多个对象是行不通的。我该怎么做?
糖果杯类比
版本 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 { ... }
}
您已创建此设置:
每个模块(sheet 纸)得到 自己的杯子 命名为 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
、A
和 A
对你没有任何帮助。相反,您可以将代码编写为:
Mod1.ts
export class Twix { ... }
Mod2.ts
export class PeanutButterCup { ... }
Mod3.ts
export class KitKat { ... }
创建如下所示的图片:
好多了!
现在,如果您还在考虑您到底有多想在模块中使用命名空间,请继续阅读...
这些不是您要寻找的概念
我们需要回到命名空间最初存在的原因,并检查这些原因是否对外部模块有意义。
组织:名称空间对于将逻辑相关的对象和类型分组在一起很方便。例如,在 C# 中,您将在 System.Collections
中找到所有集合类型。通过将我们的类型组织到分层命名空间中,我们为这些类型的用户提供了良好的“发现”体验。
名称冲突:命名空间对于避免命名冲突很重要。例如,您可能有 My.Application.Customer.AddForm
和 My.Application.Order.AddForm
—— 两种名称相同但命名空间不同的类型。在一种所有标识符都存在于同一根范围内且所有程序集都加载所有类型的语言中,将所有内容都放在命名空间中至关重要。
这些原因在外部模块中有意义吗?
Organization: 外部模块必然已经存在于文件系统中。我们必须通过路径和文件名来解析它们,因此有一个逻辑组织方案供我们使用。我们可以有一个 /collections/generic/
文件夹,里面有一个 list
模块。
名称冲突:这根本不适用于外部模块。 在 一个模块中,没有合理的理由让两个对象同名。从消费端来看,任何给定模块的 消费者 都可以选择他们将用来指代该模块的名称,因此不可能发生意外的命名冲突。
即使您认为模块的工作方式无法充分解决这些原因,尝试在外部模块中使用名称空间的“解决方案”也行不通。
盒中盒中盒中盒
一个故事:
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.
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.
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?"
"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.
模块是它们自己的盒子
您在现实生活中可能遇到过类似的事情:您在亚马逊上订购了一些东西,每件商品都出现在自己的盒子里,里面有一个较小的盒子,您的商品用自己的包装包裹。即使内盒相似,出货也没有什么用处“组合”。
用盒子类比,关键观察是外部模块是它们自己的盒子。它可能是一个具有很多功能的非常复杂的项目,但任何给定的外部模块都是它自己的盒子。
外部模块指南
既然我们已经知道我们不需要使用 'namespaces',那么我们应该如何组织我们的模块呢?下面是一些指导原则和示例。
尽可能接近顶级导出
- 如果您只导出一个 class 或函数,请使用
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
在这种情况下),并且不必做任何无关的点来找到您的对象。
- 如果要导出多个对象,请将它们全部放在顶层:
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
并将所有内容移动 'up' 一个级别)
- 具有单个
export class
或 export function
而不是 export default
的文件
- 顶层有相同
export module Foo {
的多个文件(不要以为这些会合并成一个 Foo
!)
尝试按文件夹组织:
baseTypes.ts
export class Animal {
move() { /* ... */ }
}
export class Plant {
photosynthesize() { /* ... */ }
}
dog.ts
import b = require('./baseTypes');
export class Dog extends b.Animal {
woof() { }
}
tree.ts
import b = require('./baseTypes');
class Tree extends b.Plant {
}
LivingThings.ts
import dog = require('./dog')
import tree = require('./tree')
export = {
dog: dog,
tree: tree
}
main.ts
import LivingThings = require('./LivingThings');
console.log(LivingThings.Tree)
console.log(LivingThings.Dog)
这个想法是你的模块本身不应该关心/知道他们正在参与一个命名空间,但这会以一种紧凑、明智的方式将你的 API 暴露给消费者,这种方式与哪种类型无关您用于项目的模块系统。
Ryan 的回答没有错,但是对于那些来到这里寻找如何在仍然使用 ES6 命名空间的同时维护 one-class-per-file 结构的人正确请参考 this 来自 Microsoft 的有用资源。
阅读文档后我不清楚的一件事是:如何使用 单个 import
.
导入整个(合并的)模块
编辑
回过头来更新这个答案。 TS 中出现了一些命名空间的方法。
所有模块class在一个文件中。
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 {}
}
但是当一个人从同一个命名空间导入两个 classes 时,TS 会抱怨有一个重复的标识符。这次唯一的解决办法是给命名空间加上别名。
import { Shapes } from './square';
import { Shapes as _Shapes } from './triangle';
// ugh
let myTriangle = new _Shapes.Shapes.Triangle();
这种别名绝对令人厌恶,所以不要这样做。你最好用上面的方法。就个人而言,我更喜欢 'barrel'.
Albinofrenchy 答案的小改进:
base.ts
export class Animal {
move() { /* ... */ }
}
export class Plant {
photosynthesize() { /* ... */ }
}
dog.ts
import * as b from './base';
export class Dog extends b.Animal {
woof() { }
}
things.ts
import { Dog } from './dog'
namespace things {
export const dog = Dog;
}
export = things;
main.ts
import * as things from './things';
console.log(things.dog);
dog.ts
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;
}
}
}
tree.ts
// 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 {
}
}
OP 我和你在一起。
同样,300+ 赞成票的答案也没有错,但我的意见是:
将 classes 单独放入他们舒适的温暖自己的文件中有什么问题?
我的意思是这会让事情看起来好多了对吧? (或者有人喜欢所有模型的 1000 行文件)
那么,如果要实现第一个,我们必须导入导入导入...导入每个模型文件,例如 man,srsly,一个模型文件,一个 .d.ts 文件,为什么里面有这么多*?它应该简单、整洁,仅此而已。为什么我需要在那里进口?为什么? C# 获得命名空间是有原因的。
到那时,您实际上是在使用 "filenames.ts" 作为标识符。作为标识符...现在是 2017 年了,我们仍然这样做吗?我要回到火星再睡一千年
很遗憾,我的回答是:不,如果你不使用所有这些导入或不使用这些文件名作为标识符(我认为这真的很愚蠢),你就无法使 "namespace" 发挥作用。另一种选择是:将所有这些依赖项放入一个名为 filenameasidentifier.ts 的框中并使用
export namespace(or module) boxInBox {} .
将它们包装起来,这样当它们只是简单地试图从位于它们之上的 class 获取引用时,它们就不会尝试访问具有相同名称的其他 classes。
我在这个主题周围看到的几个 questions/comments 听起来好像这个人在使用 Namespace
,他们的意思是 'module alias'。正如 Ryan Cavanaugh 在他的评论之一中提到的,您可以让一个 'Wrapper' 模块重新导出多个模块。
如果你真的想从同一个模块导入它 name/alias,在你的 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');
组织代码的正确方法是使用单独的目录代替命名空间。每个 class 都在它自己的文件中,在它各自的命名空间文件夹中。 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);
您可以使用 * as wrapper_var
语法使所有导入的方法都可以在 wrapper_var
:
下访问
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();
我有一些代码:
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
。跨文件在同一个命名空间中组合多个对象是行不通的。我该怎么做?
糖果杯类比
版本 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 { ... }
}
您已创建此设置:
每个模块(sheet 纸)得到 自己的杯子 命名为 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
、A
和 A
对你没有任何帮助。相反,您可以将代码编写为:
Mod1.ts
export class Twix { ... }
Mod2.ts
export class PeanutButterCup { ... }
Mod3.ts
export class KitKat { ... }
创建如下所示的图片:
好多了!
现在,如果您还在考虑您到底有多想在模块中使用命名空间,请继续阅读...
这些不是您要寻找的概念
我们需要回到命名空间最初存在的原因,并检查这些原因是否对外部模块有意义。
组织:名称空间对于将逻辑相关的对象和类型分组在一起很方便。例如,在 C# 中,您将在 System.Collections
中找到所有集合类型。通过将我们的类型组织到分层命名空间中,我们为这些类型的用户提供了良好的“发现”体验。
名称冲突:命名空间对于避免命名冲突很重要。例如,您可能有 My.Application.Customer.AddForm
和 My.Application.Order.AddForm
—— 两种名称相同但命名空间不同的类型。在一种所有标识符都存在于同一根范围内且所有程序集都加载所有类型的语言中,将所有内容都放在命名空间中至关重要。
这些原因在外部模块中有意义吗?
Organization: 外部模块必然已经存在于文件系统中。我们必须通过路径和文件名来解析它们,因此有一个逻辑组织方案供我们使用。我们可以有一个 /collections/generic/
文件夹,里面有一个 list
模块。
名称冲突:这根本不适用于外部模块。 在 一个模块中,没有合理的理由让两个对象同名。从消费端来看,任何给定模块的 消费者 都可以选择他们将用来指代该模块的名称,因此不可能发生意外的命名冲突。
即使您认为模块的工作方式无法充分解决这些原因,尝试在外部模块中使用名称空间的“解决方案”也行不通。
盒中盒中盒中盒
一个故事:
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.
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.
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?"
"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.
模块是它们自己的盒子
您在现实生活中可能遇到过类似的事情:您在亚马逊上订购了一些东西,每件商品都出现在自己的盒子里,里面有一个较小的盒子,您的商品用自己的包装包裹。即使内盒相似,出货也没有什么用处“组合”。
用盒子类比,关键观察是外部模块是它们自己的盒子。它可能是一个具有很多功能的非常复杂的项目,但任何给定的外部模块都是它自己的盒子。
外部模块指南
既然我们已经知道我们不需要使用 'namespaces',那么我们应该如何组织我们的模块呢?下面是一些指导原则和示例。
尽可能接近顶级导出
- 如果您只导出一个 class 或函数,请使用
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
在这种情况下),并且不必做任何无关的点来找到您的对象。
- 如果要导出多个对象,请将它们全部放在顶层:
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
并将所有内容移动 'up' 一个级别) - 具有单个
export class
或export function
而不是export default
的文件
- 顶层有相同
export module Foo {
的多个文件(不要以为这些会合并成一个Foo
!)
尝试按文件夹组织:
baseTypes.ts
export class Animal {
move() { /* ... */ }
}
export class Plant {
photosynthesize() { /* ... */ }
}
dog.ts
import b = require('./baseTypes');
export class Dog extends b.Animal {
woof() { }
}
tree.ts
import b = require('./baseTypes');
class Tree extends b.Plant {
}
LivingThings.ts
import dog = require('./dog')
import tree = require('./tree')
export = {
dog: dog,
tree: tree
}
main.ts
import LivingThings = require('./LivingThings');
console.log(LivingThings.Tree)
console.log(LivingThings.Dog)
这个想法是你的模块本身不应该关心/知道他们正在参与一个命名空间,但这会以一种紧凑、明智的方式将你的 API 暴露给消费者,这种方式与哪种类型无关您用于项目的模块系统。
Ryan 的回答没有错,但是对于那些来到这里寻找如何在仍然使用 ES6 命名空间的同时维护 one-class-per-file 结构的人正确请参考 this 来自 Microsoft 的有用资源。
阅读文档后我不清楚的一件事是:如何使用 单个 import
.
编辑 回过头来更新这个答案。 TS 中出现了一些命名空间的方法。
所有模块class在一个文件中。
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 {}
}
但是当一个人从同一个命名空间导入两个 classes 时,TS 会抱怨有一个重复的标识符。这次唯一的解决办法是给命名空间加上别名。
import { Shapes } from './square';
import { Shapes as _Shapes } from './triangle';
// ugh
let myTriangle = new _Shapes.Shapes.Triangle();
这种别名绝对令人厌恶,所以不要这样做。你最好用上面的方法。就个人而言,我更喜欢 'barrel'.
Albinofrenchy 答案的小改进:
base.ts
export class Animal {
move() { /* ... */ }
}
export class Plant {
photosynthesize() { /* ... */ }
}
dog.ts
import * as b from './base';
export class Dog extends b.Animal {
woof() { }
}
things.ts
import { Dog } from './dog'
namespace things {
export const dog = Dog;
}
export = things;
main.ts
import * as things from './things';
console.log(things.dog);
dog.ts
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;
}
}
}
tree.ts
// 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 {
}
}
OP 我和你在一起。 同样,300+ 赞成票的答案也没有错,但我的意见是:
将 classes 单独放入他们舒适的温暖自己的文件中有什么问题? 我的意思是这会让事情看起来好多了对吧? (或者有人喜欢所有模型的 1000 行文件)
那么,如果要实现第一个,我们必须导入导入导入...导入每个模型文件,例如 man,srsly,一个模型文件,一个 .d.ts 文件,为什么里面有这么多*?它应该简单、整洁,仅此而已。为什么我需要在那里进口?为什么? C# 获得命名空间是有原因的。
到那时,您实际上是在使用 "filenames.ts" 作为标识符。作为标识符...现在是 2017 年了,我们仍然这样做吗?我要回到火星再睡一千年
很遗憾,我的回答是:不,如果你不使用所有这些导入或不使用这些文件名作为标识符(我认为这真的很愚蠢),你就无法使 "namespace" 发挥作用。另一种选择是:将所有这些依赖项放入一个名为 filenameasidentifier.ts 的框中并使用
export namespace(or module) boxInBox {} .
将它们包装起来,这样当它们只是简单地试图从位于它们之上的 class 获取引用时,它们就不会尝试访问具有相同名称的其他 classes。
我在这个主题周围看到的几个 questions/comments 听起来好像这个人在使用 Namespace
,他们的意思是 'module alias'。正如 Ryan Cavanaugh 在他的评论之一中提到的,您可以让一个 'Wrapper' 模块重新导出多个模块。
如果你真的想从同一个模块导入它 name/alias,在你的 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');
组织代码的正确方法是使用单独的目录代替命名空间。每个 class 都在它自己的文件中,在它各自的命名空间文件夹中。 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);
您可以使用 * as wrapper_var
语法使所有导入的方法都可以在 wrapper_var
:
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();