4

Can you help me out - is this a bug or am I misunderstanding the documentation?

I have two files in a namespace - you'll find a simpler version below.

The Typescript docs state that "Even though the files are separate, they can each contribute to the same namespace and can be consumed as if they were all defined in one". Therefore I would assume that a class from one file could access another file from within the same namespace even if it was not exported. (Because that would be the case if they were defined in the same place)

However, tsc complains that it can't find the name "Dispatcher". Why does this happen? Am I misunderstanding the documentation at this point? Or is it just a compiler bug? Would be a shame if it was the former, because namespace only visibility would help a lot for unit testing and encapsulating.

Code:

(code changed for simplicity. If syntax errors are present, they are caused by that):

Application.ts:

/// <reference path="Dispatcher.ts"/>
namespace Application {
    export class Application {
        constructor() {
            new Dispatcher(); 
        }
    }
}

Dispatcher.ts:

namespace Application {
    class Dispatcher { /* ... */ }
}
1
  • No, members must be exported in order to "contribute" to a namespace across different files. If you look at the generated JavaScript code you will see why this is, all values are declared inside an IIFE and so unexported values are only visible inside that specific function's context. Also, without this behaviour you couldn't have two private properties with the same name across any file that contributes to that namespace, which would be undesirable. Commented Sep 20, 2018 at 17:52

1 Answer 1

6

The Typescript docs state that "Even though the files are separate, they can each contribute to the same namespace and can be consumed as if they were all defined in one".

It depends on the precise definition of "contribute to the same namespace". Current interpretation is "anything exported is available anywhere, anything not exported is available only inside the namespace part where it's defined". This is how it's explained in the language specification:

Namespaces provide a mechanism for organizing code and declarations in hierarchies of named containers. Namespaces have named members that each denote a value, a type, or a namespace, or some combination thereof, and those members may be local or exported. The body of a namespace corresponds to a function that is executed once, thereby providing a mechanism for maintaining local state with assured isolation. Namespaces can be thought of as a formalization of the immediately-invoked function expression (IIFE) pattern.

If there are several declarations of the same namespace, each one is compiled into a separate IIFE, as can be seen in compiled javascript code for Dispatcher.ts:

var Application;
(function (Application) {
    class Dispatcher {
    }
})(Application || (Application = {}));

The result is that Dispatcher is scoped inside the function, and it's not accessible from the outside.

Even if you had namespace Application repeated twice in the same file, each would be defining its own copy of Dispatcher - there is no error for this code:

namespace Application {
    class Dispatcher { /* ... */ }
}

namespace Application {
    class Dispatcher { /* ... */ }
}

The only way to make Dispatcher accessible from outside namespace { ... } is to export it. With export, Dispatcher becomes a property of Application object:

namespace Application {
    export class Dispatcher { /* ... */ }
}

compiled code:

var Application;
(function (Application) {
    class Dispatcher {
    }
    Application.Dispatcher = Dispatcher;
})(Application || (Application = {}));

This behavior makes sense implementation-wise. It would be really troublesome to implement namespace so that un-exported objects were shared among all namespace instances, but not accessible from outside.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for the quick answer. One more question though: If I understand correctly, I am not declaring modules anywhere - there are just namespaces and reference directives. Is there any implicit conversion to modules happening?
wow! thanks a lot that was an awesome answer! :) really what i wanted to know!

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.