Typescript Namespaces

In a large application it is common to separate divisions of the app into folders (or namespaces). Say I have two folders, Admin and Maint and these two folders have a dialog with the same name, say EditOptionsDlg. Typescript allows you to wrap your code in a namespace so you could refer to these components as Admin.EditOptionsDlg and Maint.EditOptionsDlg.

Is this possible in Aurelia and will it mess up the convention approach to loading?

1 Like

We intend for anything that works natively and in TypeScript, to work in Aurelia. There is no reason for namespaces not to work.

TS namespaces are turned into synthetic modules (IIFE that returns an object literal with the containing exports) in a similar way that webpack does with native modules.

However,

In a large application it is common to separate divisions of the app into folders (or namespaces).

I would say it is not common at all to divide a large app into namespaces. In fact very few modern TS projects still do this.
Folders, for sure, but combining that with namespaces can result in some rather awkward situations that stray far from how modules are supposed to work.

Why not just import/export modules the native way, and use a import * as namespace from './folder/index' if you have a lot of stuff to use from a module?

2 Likes

@fkleuver Fred, it may not be the way in modern TS projects, but I have a .NET WPF desktop application that uses namespaces the way I articulated, and it is the recommended/accepted way to partition the code. My Aurelia app is the same app but runs in the browser instead of desktop. When reading the TypeScript docs on namespaces, it paralleled what we do with WPF, so that is why I asked if Aurelia would have a problem with it.

My concern was the Aurelia convention of loading the view with the same name as the module, if it would still work if preceded with a namespace. Using an import would be simple as you stated. But what about Aurelia conventions (views and view-models)?

1 Like

You seem to believe that namespaces solve a problem that can only be solved using namespaces, but keep in mind that they originate from before ES6 modules were standardized.

Apart from having a more familiar name, they’re in no way whatsoever better suited to solve the problem of namespacing than modules do.

Example:

Namespaces

things/foo.ts

namespace things {
  export class Foo {}
}

things/bar.ts

/// <reference path="foo.ts" />
namespace things {
  export class Bar {
    constructor(foo: Foo) {}
  }
}

things/baz.ts

/// <reference path="foo.ts" />
namespace things {
  export class Baz {
    constructor(foo: Foo) {}
  }
}

app.ts

/// <reference path="things/foo.ts" />
/// <reference path="things/bar.ts" />
/// <reference path="things/baz.ts" />
export class App {
  constructor(foo: things.Foo, bar: things.Bar, baz: things.Baz) {}
}

Modules

things/foo.ts

export class Foo {}

things/bar.ts

import { Foo } from './foo';
export class Bar {
  constructor(foo: Foo) {}
}

things/baz.ts

import { Foo } from './foo';
export class Baz {
  constructor(foo: Foo) {}
}

things/index.ts

export * from './foo';
export * from './bar';
export * from './baz';

app.ts

import * as things from './things/index';
export class App {
  constructor(foo: things.Foo, bar: things.Bar, baz: things.Baz) {}
}

The above is just one of the many possible flavors of how you can do namespacing in modules. This example puts the responsibility of naming the namespace with the consumer and leaves you with the freedom to import things directly as well.

Alternatively, you can ‘force’ your fellow team mates to stick to the namespace like so:

things/index.ts

import { Foo } from './foo';
import { Bar } from './bar';
import { Baz } from './baz';
export const things = { Foo, Bar, Baz };

app.ts

import { things } from './things/index';
export class App {
  constructor(foo: things.Foo, bar: things.Bar, baz: things.Baz) {}
}

Or if you don’t mind the extra boilerplate in order to keep the object a true module object:

things/index-exports.ts

export * from './foo';
export * from './bar';
export * from './baz';

things/index.ts

export * as things from './index-exports';

Conclusion

The module examples I gave you are just some of the many things you can do with modules. The good thing is they’re native, and thus natively supported by any tool or framework.

You’ll get VS Code intellisense and auto import suggestions, auto update refactorings, and you get none of this with namespaces: you’ll have to write those /// references yourself by hand and keep them up to date by hand.

And you don’t have to wonder whether it will work with Aurelia (or anything else for that matter).

For the record: I haven’t tried and I don’t know of anyone who has. You’d have to try. I don’t see why not, but again, I haven’t tried.

But when you’re talking about a large you’re probably also talking about an expensive project with high stakes. Do you want something that is robust, standardized, and by default supported by the entire post-ES6 ecosystem or do you want a legacy feature that happens to look and feel a bit more familiar?

3 Likes

Now that you have cleared things up for me I can certainly try myself. We have commercial software which is crucial to our business. I have 18 years experience with dotnet, in which large applications are split into “assemblies” and literally everything is in a namespace (not our design, but fundamental within the .NET framework).

Until Aurelia CLI/TypeScript, web development was not viable for our application. But I’ve found that Aurelia is simple and powerful and its community supportive. Since my background in JavaScript is minimal, I struggle a bit with how to do things in this new world. But I’m getting there, having developed many custom elements and making use of Aurelia Dialog, our “browser based” application is very close to our WPF desktop application, which is astounding.

Thank you for Aurelia, I’m excited for v2!

6 Likes