在 TypeScript 中使用分布在多个模块文件中的命名空间

Using namespace spread over multiple module files in TypeScript

我已经开始从事一个大型打字稿项目。

从一开始,我就想让我的文件井井有条(这个项目将由许多开发人员拆分,因此顺序非常必要)。

我一直在尝试使用模块/命名空间并将 类 分成单独的文件,每个文件都有一个包含命名空间的文件夹。

文件结构为:

app.ts
\Classes
---- \Animals
---- ---- Mammals.ts
---- ---- Reptiles.ts

然后我尝试使用类似 import * as Animals from "./Classes/Animals"

的方式导入 app.ts 中该命名空间中的所有文件

至于命名空间文件本身,我尝试了以下方法,但没有成功:

namespace Animals {
    export class Mammals {
        constructor() {
        }
    }
}

还有:

module Animals {
    export class Reptiles {
        constructor() {
        }
    }
}

不幸的是,该路径从未被识别(因为它指向一个文件夹而不是单个文件)。这可能吗?将我的所有 类 来自一个文件中的单个名称空间将导致文件长达数千行,并且对于该项目而言不可维护。

我还注意到 TypeScript 1.5 支持 tsconfig.json - 但是,当开发人员开始添加 类 时,必须手动将每个文件添加到地图是引入问题的必经之路.

注意:我使用的是 Visual Studio 2015,TypeScript 1.5(我相信,不确定如何验证)。我也打开了 ES6 支持。

外部模块意味着您逐个文件加载模块。 AMD 和 CommonJS 都没有命名空间这样的东西。您可以使用某种后处理将文件捆绑在一个模块中。


下面定义了一个内部模块:

module Animals {
    export class Reptiles {
        constructor() {
        }
    }
}

你不应该为它使用 importAnimals.Reptiles 随处可见。唯一的目的是以正确的顺序加载脚本(例如,继承者之前的 base 类)。所以你应该列出 ts.config 或其他地方的所有文件。在我的项目中,我在文件夹上使用捆绑包,并约定将 @ 添加到基本 类.

的文件名中

另一种解决方案是使用外部模块:AMD (RequireJS) or CommonJS (Browserify)。在这种情况下,从声明中删除上层 module。如果一个文件只包含一种类型,您可以将其导出为根目录:

class Reptiles {
    constructor() {
    }
}

export = Reptiles;

您可以通过文件路径引用模块:

import Reptilies = require('..\Animals\Reptilies')
var  reptile = new Reptile();

或者使用新的 ES6 模块:

export class Reptiles {
    constructor() {
    }
}

import { Reptiles } from '..\Animals\Reptilies';

使用重新导出来创建一个外部模块,该模块对来自其他模块的类型进行分组和公开:

// Classes/Animals.ts
export * from '.\Animals\Mammals';
export * from '.\Animals\Reptiles';

然后照常从新模块导入类型:

// app.ts
import * as Animals from '.\Classes\Animals'

let dog: Animals.Dog;
let snake: Animals.Snake;

或者

// app.ts
import { Dog, Snake } from '.\Classes\Animals'

let dog: Dog;
let snake: Snake;

找到了实现目标的方法,但不是使用命名空间关键字。

  1. namespace下的"Animals" 类, Animal.ts & Mammal.ts & Reptile.ts.
  2. 桶用 index.ts。
  3. animals.ts 命名空间分组。

样本类:

index.ts(作为桶)

animals.ts(用于命名空间分组)

这里是命名空间的概念。

似乎没有办法单独使用名称空间来做到这一点(除非您想使用 Module Augmentationdeclare 每个新项目单独添加);但是,名称空间可以是 class 的一部分,可以扩展!这是我能找到的最佳选择:

CoreLibraryTypes.ts

abstract class Types { }
namespace Types { 
    export class TypeA { }
    export class TypeB { }
    export class TypeC { }
}
export { Types };

CoreTypesExtended.ts

import CoreLibraryTypes from "./CoreLibraryTypes";

abstract class Types extends CoreLibraryTypes { }
namespace Types { 
    export class TypeD { }
    export class TypeE { }
    export class TypeF { }
}
export { Types };

当然,缺点是只有导入第二个模块才会添加新类型。第一个模块将保持原样。理想情况下,"update" 具有附加类型(例如来自插件)的类型的命名空间会很好,这样模块扩充就更自然地得到支持(而不是必须手动编写),但我想那将不得不做,直到有人意识到通过手动声明更新的定义来增加模块只是做命名空间已经做的事情的半美元方式 lol(包括 classes,如上所示,它已经可以使用命名空间合并作为class)。 ;)

注意:在上面的示例中,我使用 export { Types }; 是有原因的 - 这将允许其他人扩充我的模块。默认导出不支持扩充(除非需要 - 几乎可以密封它)。

如果您有自己的库并且想从命名空间导出多个文件,您可以这样做:

// classes/animals/mammals.ts
export enum Mammals {
  cow = 'cow',
  goat = 'goat',
}

// classes/animals/reptiles.ts
export interface Reptile {
  isOurOverlord: boolean;
}
export function isOurOverlord(specimen: Reptile) { ... } 

// classes/animals/index.ts
import * as mammals from './mammals';
import * as reptiles from './reptiles';
export { mammals, reptiles };

// classes/index.ts
import * as animals from './animals';
export { animals };

// app.ts
import { animals } from './classes';
const reptile: animals.reptiles.Reptile = {
  isOurOverlord: animals.reptiles.isOurOverlord(...),
}

编辑:也就是说,您不需要 typescript 的命名空间来使用 animals.reptiles.Reptile 的超级方便语法,用于同一“命名空间”中的类型和值 animals.mammals.Mammals