snippetjavascriptCritical
How do I use namespaces with TypeScript external modules?
Viewed 0 times
externaltypescriptwithhownamespacesusemodules
Problem
I have some code:
baseTypes.ts
dog.ts
tree.ts
This is all very confusing. I want to have a bunch of external modules all contribute types to the same namespace,
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 {
}
}This is all very confusing. I want to have a bunch of external modules all contribute types to the same namespace,
Living.Things. It seems that this doesn't work at all -- I can't see Animal in dogs.ts. I have to write the full namespace name b.Living.Things.Plant in tree.ts. It doesn't work to combine multiple objects in the same namespace across file. How do I do this?Solution
Candy Cup Analogy
Version 1: A cup for every candy
Let's say you wrote some code like this:
Mod1.ts
Mod2.ts
Mod3.ts
You've created this setup:
Each module (sheet of paper) gets its own cup named
Version 2: One cup in the global scope
If you weren't using modules, you might write code like this (note the lack of
global1.ts
global2.ts
global3.ts
This code creates a merged namespace
This setup is useful, but doesn't apply in the case of modules (because modules don't pollute the global scope).
Version 3: Going cupless
Going back to the original example, the cups
Mod1.ts
Mod2.ts
Mod3.ts
to create a picture that looks like this:
Much better!
Now, if you're still thinking about how much you really want to use namespace with your modules, read on...
These Aren't the Concepts You're Looking For
We need to go back to the origins of why namespaces exist in the first place and examine whether those reasons make sense for external modules.
Organization: Namespaces are handy for grouping together logically-related objects and types. For example, in C#, you're going to find all the collection types in
Name Conflicts: Namespaces are important to avoid naming collisions. For example, you might have
Do those reasons make sense in external modules?
Organization: External modules are already present in a file system, necessarily. We have to resolve them by path and filename, so there's a logical organization scheme for us to use. We can have a
Name Conflicts: This doesn't apply at all in external modules. Within a module, there's no plausible reason to have two objects with the same name. From the consumption side, the consumer of any given module gets to pick the name that they will use to refer to the module, so accidental naming conflicts are impossible.
Even if you don't believe that those reasons are adequately addressed by how modules work, the "solution" of trying to use namespaces in external modules doesn't even work.
Boxes in Boxes in Boxes
A story:
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
Bob is a very confused man.
Modules are Their Own Box
You've probably had something similar happen in real life: You order a few thing
Version 1: A cup for every candy
Let's say you wrote some code like this:
Mod1.ts
export namespace A {
export class Twix { ... }
}Mod2.ts
export namespace A {
export class PeanutButterCup { ... }
}Mod3.ts
export namespace A {
export class KitKat { ... }
}You've created this setup:
Each module (sheet of paper) gets its own cup named
A. This is useless - you're not actually organizing your candy here, you're just adding an additional step (taking it out of the cup) between you and the treats.Version 2: One cup in the global scope
If you weren't using modules, you might write code like this (note the lack of
export declarations):global1.ts
namespace A {
export class Twix { ... }
}global2.ts
namespace A {
export class PeanutButterCup { ... }
}global3.ts
namespace A {
export class KitKat { ... }
}This code creates a merged namespace
A in the global scope:This setup is useful, but doesn't apply in the case of modules (because modules don't pollute the global scope).
Version 3: Going cupless
Going back to the original example, the cups
A, A, and A aren't doing you any favors. Instead, you could write the code as:Mod1.ts
export class Twix { ... }Mod2.ts
export class PeanutButterCup { ... }Mod3.ts
export class KitKat { ... }to create a picture that looks like this:
Much better!
Now, if you're still thinking about how much you really want to use namespace with your modules, read on...
These Aren't the Concepts You're Looking For
We need to go back to the origins of why namespaces exist in the first place and examine whether those reasons make sense for external modules.
Organization: Namespaces are handy for grouping together logically-related objects and types. For example, in C#, you're going to find all the collection types in
System.Collections. By organizing our types into hierarchical namespaces, we provide a good "discovery" experience for users of those types.Name Conflicts: Namespaces are important to avoid naming collisions. For example, you might have
My.Application.Customer.AddForm and My.Application.Order.AddForm -- two types with the same name, but a different namespace. In a language where all identifiers exist in the same root scope and all assemblies load all types, it's critical to have everything be in a namespace.Do those reasons make sense in external modules?
Organization: External modules are already present in a file system, necessarily. We have to resolve them by path and filename, so there's a logical organization scheme for us to use. We can have a
/collections/generic/ folder with a list module in it.Name Conflicts: This doesn't apply at all in external modules. Within a module, there's no plausible reason to have two objects with the same name. From the consumption side, the consumer of any given module gets to pick the name that they will use to refer to the module, so accidental naming conflicts are impossible.
Even if you don't believe that those reasons are adequately addressed by how modules work, the "solution" of trying to use namespaces in external modules doesn't even work.
Boxes in Boxes in Boxes
A story:
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.
Modules are Their Own Box
You've probably had something similar happen in real life: You order a few thing
Code Snippets
export namespace A {
export class Twix { ... }
}export namespace A {
export class PeanutButterCup { ... }
}export namespace A {
export class KitKat { ... }
}namespace A {
export class Twix { ... }
}namespace A {
export class PeanutButterCup { ... }
}Context
Stack Overflow Q#30357634, score: 1190
Revisions (0)
No revisions yet.