【问题标题】:Using namespace spread over multiple module files in TypeScript在 TypeScript 中使用分布在多个模块文件中的命名空间
【发布时间】:2015-11-06 02:59:34
【问题描述】:

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

从一开始,我就想让我的文件井井有条(这个项目将在很多开发人员之间进行拆分,所以顺序非常必要)。

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

文件结构为:

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 支持。

【问题讨论】:

  • 官方手册涵盖了这个(虽然ut可能已经过时)typescriptlang.org/Handbook#modules-splitting-across-files
  • 谢谢@pablochan - 我也看过那个。像这样引用命名空间中的每个文件是违反直觉的。是否可以指向命名空间而不是单个文件?
  • 否,除非 Visual Studio 或其他工具对此提供支持。

标签: javascript typescript typescript1.5


【解决方案1】:

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


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

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

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

另一种解决方案是使用外部模块:AMD (RequireJS) 或 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';

【讨论】:

  • 在这种情况下,我将如何导入 Animals 命名空间?如果我有一个包含 20 个类(将全部使用)的命名空间,我可能需要一个参考。至少其他语言是这样的。另外,我注意到在 ES 6 支持下,export = Reptiles; 代码似乎不鼓励使用,因为它不可升级
【解决方案2】:

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

// 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;

【讨论】:

  • 这不再起作用了吗?这正是我想做的,但我不断收到“...Mammals.ts 不是模块”
【解决方案3】:

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

  1. 命名空间下的“Animals”类,Animal.ts & Mammal.ts & Reptile.ts。
  2. 带有用于桶的 index.ts。
  3. animals.ts 用于命名空间分组。

示例类:

index.ts(如桶)

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

这里是命名空间的概念。

【讨论】:

  • 对我来说这是正确的答案,鉴于桶已经到位,您现在可以拥有多级命名空间,我一直在寻找类似 api.Animals.Dog 的东西,但是这让我拥有了它如果有办法在这种方法中使用“命名空间”关键字,那就更好了
  • 解决问题的好方法,谢谢!是的,TS 有命名空间,但我们不能真正将它们用作命名空间,os 有什么隐藏的方法吗?
  • 考虑到/// <reference path="filename.ts" /> 语法的引入,这个答案是否仍然是最新的?
【解决方案4】:

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

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 };

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

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

【讨论】:

    【解决方案5】:

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

    // 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

    【讨论】:

      猜你喜欢
      • 2013-11-04
      • 2021-01-22
      • 1970-01-01
      • 2023-03-14
      • 1970-01-01
      • 2021-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多