49

I've started work on a large-scale typescript project.

Right from the outset, I want to keep my files organized (this project will be split between lots of developers so order is very necessary).

I have been attempting to use modules / namespaces and splitting classes out into separate files for each one, with a folder holding the namespace.

The file structure is:

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

I then attempt to import all files in that namespace in app.ts using something like: import * as Animals from "./Classes/Animals"

As for the namespace files themselves, I have tried the following, with no success:

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

and also:

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

Unfortunately, the path is never recognized (as it points to a folder and not a single file). Is this even possible? Having all my classes from a single namespace in one file will result in files which are thousands of lines long and for this project that is not maintainable.

I have also noticed that TypeScript 1.5 has support for tsconfig.json - however, having to add each file manually to the map is a sure-fire way of introducing issues when developers start adding classes.

NOTE: I'm using Visual Studio 2015, TypeScript 1.5 (I believe, not sure how to verify). I also have ES6 support turned on.

3
  • The official handbook covers this (although ut may be outdated) typescriptlang.org/Handbook#modules-splitting-across-files Commented Aug 13, 2015 at 8:36
  • Thanks @pablochan - I had seen that one too. Referencing each file in the namespace like that is counter intuitive. Is it possible to point to the namespace rather than individual files? Commented Aug 13, 2015 at 8:39
  • No, unless Visual Studio or some other tool has suport for that. Commented Aug 13, 2015 at 8:42

5 Answers 5

37

Use re-exporting to create an external module that groups and exposes types from other modules:

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

Then import the types from the new module as usual:

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

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

Or

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

let dog: Dog;
let snake: Snake;
Sign up to request clarification or add additional context in comments.

2 Comments

Does this not work anymore? This is exactly what I would like to do, but I keep getting "...Mammals.ts is not a module"
A file is only considered a module, if at least one of the two following statements are true: 1. it has imports or 2. it has exports. If both statements are not true regarding a file, the file is not considered an es-module.
29

Found a way to achieve your goal but not with the namespace keyword.

  1. The "Animals" classes, Animal.ts & Mammal.ts & Reptile.ts under namespace.
  2. with index.ts for the barrel.
  3. animals.ts for namespace grouping.

Animal namespace

Sample Classes:

Classes

index.ts (as barrel)

enter image description here

animals.ts (for namespace grouping)

enter image description here

And here you go of the concept of the namespace.

enter image description here

3 Comments

for me this was the correct answer, given the barrels are in place you can now have multi-level namespaces, I was looking for something like api.Animals.Dog and this let me have that, however it was even nicer if there was a way to use "namespace" keyword in this approach
Great way to solve the problem thanks! Yeah, TS has namespaces but we can't really use them as namespaces, os is there some hidden way?
Is this answer still current given the introduction of the /// <reference path="filename.ts" /> syntax?
5

If you have your own library and you want to export the multiple files like from namespace, you can do this:

// 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(...),
}

edit: i.e. you don't need typescript's namespaces in order to use that super convenient syntax of animals.reptiles.Reptile for types and values animals.mammals.Mammals within the same "namespace".

Comments

3

Seems there is no way to do this using namespaces on their own (unless you want to use Module Augmentation and declare every new item to add separately); however, a namespace can be part of a class, which can be extended! This is the best alternative I can find:

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

The downside, of course, is that only the import of the second module will have the new types added. The first module will remain as before. Ideally it would be nice to "update" a namespace of types with additional types (like from plugins), such that module augmentation was more naturally supported (instead of having to write it by hand), but I guess that will have to do until someone realizes augmentation of modules by manually declaring updated definitions is just a half-a$$ way to do what namespaces already do lol (including classes, as seen above, which can already use namespace merging as part of the class). ;)

Note: In the example above I used export { Types }; for a reason - this will allow others to augment my modules. Augmentation is not supported for default exports (unless that is desired - sort of seals it virtually).

Comments

0

External modules imply that you load modules file by file. Both AMD and CommonJS do not have such thing as namespace. You can use some kind of postprocessing to bundle files in one module.


The following defines an internal module:

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

You shouldn't use import for it. Animals.Reptiles is visible anywhere. The only aim is to load scripts in the proper order (e.g. base classes before heritors). So you should list all files in ts.config or somewhere else. In my project I use bundles on folders and have a convention to add @ to filenames of base classes.

Another solution is to use external modules: AMD (RequireJS) or CommonJS (Browserify). In that case remove upper level module from declaration. If one file contains only one type you can export it as a root:

class Reptiles {
    constructor() {
    }
}

export = Reptiles;

You can refer module by file path:

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

Or with new ES6 modules:

export class Reptiles {
    constructor() {
    }
}

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

1 Comment

In this case, how would I import the Animals namespace? If I have a namespace with 20 classes (which will all be used), I might want one reference. At least that's the case with other languages. Also, I've noticed that with ES 6 support, the export = Reptiles; code seems to be discouraged as it's not upgradeable

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.