How can I get an alias to a deeply-nested namespace that has no concrete class?

I have a typings file that is chock-full of deeply-nested namespaces, generated from a pile of C# classes. (This typings file is already used by other code built around internal modules. I'm in the process of eliminating that other code, but it's going to be a while, and I have some code that needs to run right now.)

The typings file looks like this:

declare namespace Thing.Stuff.WhosOnFirst.WhatsOnSecond {
    export interface IDontKnowsOnThird {
        ... lots of interfaces, no classes. In short, *nothing* that turns into JavaScript.

That's terribly awkward to use, of course:

public findShortStop(
    thirdBase: Thing.Stuff.WhosOnFirst.WhatsOnSecond.IDontKnowsOnThird, 
    pitcher: Thing.Stuff.WhosOnFirst.WhatsOnSecond.Tomorrow
) {

So I want to use an import statement as an alias. By wild coincidence, I've always been able to get away with statements like this, because "Thing" actually exists in my work:

import Stuff = Thing.Stuff;

But it turns out that I can't do this with my deeply nested classes:

import WhatsOnSecond = Thing.Stuff.WhosOnFirst.WhatsOnSecond;
let baseman: WhatsOnSecond.IDontKnowsOnThird = {};

The above code generates JavaScript that looks like this:

WhatsOnSecond = Thing.Stuff.WhosOnFirst.WhatsOnSecond;
var baseman = {};

And that's a huge problem: here's the error that shows up in the console.

Uncaught (in promise) Error: (SystemJS) TypeError: Cannot read property 'WhosOnFirst' of undefined
    at execute (http://localhost/Scripts/Shared/utils.js?v=180907165808:10:59)

As it happens, "Thing" exists. But "Stuff" doesn't. So when I make an alias for Stuff, you get valid (if nonsensical) code, and I'm allowed to use all of the types in the Thing.Stuff namespace via that alias, so Stuff.WhosOnFirst is just fine:

Stuff = Thing.Stuff; // evaluates to undefined. Compiler likes me, runtime likes me, no errors.

But doing that with further-nested properties is a complete failure, because trying to access properties of "undefined" is a JavaScript error, halting execution of the script.

So: How can I create these import aliases to let me reference deeply-nested namespaces, so I don't have to type out the whole namespace every time I reference an interface?

Typing out namespaces with six words in them every time I use an interface gets old really fast.

2 answers

  • answered 2018-07-11 06:27 Paleo

    You can use:

    type Stuff = Thing.Stuff;
    type WhatsOnSecond = Thing.Stuff.WhosOnFirst.WhatsOnSecond;

  • answered 2018-07-11 20:22 PotatoEngineer

    After further flailing, I ended up with this, which correctly aliases the namespace and lets me properly access enums under that namespace:

    import "../../Path/To/.d.ts/File/With/Compiled/Suffix/myTypings.js";
    import WhatsOnSecond = Thing.Stuff.WhosOnFirst.WhatsOnSecond;
    let someEnum: WhatsOnSecond.SomeEnum = WhatsOnSecond.SomeEnum.AnEnumValue;

    The import of the .js file at the top brings all of the namespace-setup code into the browser, which in turn lets me set up the following namespace-alias-via-import of WhatsOnSecond.

    I'm not particularly happy with this hack, since I'm dragging in implementation details, but it has the virtue of actually working.