How do I re-export an imported module that has interfaces in it in a usable manner?

Viewed 266

I had one file with a large number of modules in it. I wanted to separate those modules into different files and expose them from the original file. This is because the original file is >800 loc and becoming very difficult to manage.

So I have created a file that contains a module with functions and interfaces:

export module Subsection1 {
    
    export interface Interface {
        id: string;
    }
    
    export function DoInterfaceStuff(obj: any) {
        return = <Interface>{ id: obj }
    }
}

I have the


import { Subsection1 as Subsection1Base } from "./Subsection1";


export module GodManager {

    export type Subsection1 = Subsection1Base & (typeof Subsection1Base);
    export var Subsection1 = Subsection1Base;

    var ObjByID = new Map<string, Subsection1.Interface>();
    export function DoThing(source) {
        ObjByID.set(source.id, Subsection1.DoInterfaceStuff(source.origin));
    }
}

NOTE The new name and variable must be accessible externally to the current class because I'm actually loading Subsection1Base lazily (mostly to avoid a circular dependancy).

2 Answers

Prelude

The question changed the example code but they originally had exported a module
and interface with the same name and tried to reference the module in a context where only the interface was valid. While it is not necessarily incorrect to share a name between two different declarations of separate language constructs, it did obscure the true issue they were experiencing (see original answer below).

Original Answer

If we clean up the naming of your types, we can figure out pretty quickly what's wrong here.

In TriggerEvent, we rename the interface Trigger to ITrigger:

export interface ITrigger {
    // ..
}

export module Trigger {
    //...
    
    export function Serialise(restoredEvent: ITrigger) {
        //...
    }
}

Now, in ActionManager, we can see what went wrong:

import { Trigger as TriggerBase } from "./TriggerEvent";

export module ActionManager {

    type Trigger = TriggerBase & (typeof Trigger); 
    //             ^^^^^^^^^^^
    // ERROR - Cannot use namespace 'TriggerBase' as a type.
    var Trigger = TriggerBase;

    var EventsByID = new Map<UUID, Trigger>();
    var PrepreparedInputs = new Map<UUID, Trigger.Serialised>(); 
    //                                    ^^^^^^^ 
    // ERROR - 'Trigger' only refers to a type, but is being used as a namespace here.
}

You cannot use a namespace (module) as a type. (see the docs). Before, since the interface and the module were named the same, TypeScript went with the context-appropriate option, the interface. Ergo, no errors there! Regardless, we still have the original error 'Trigger' only refers to a type... because a type is a type, and not a namespace (or module).

Finally, ActionManager.Trigger.Serialise(stuff); isn't working because Trigger isn't exported from the ActionManager module.

Solution

Since you can't assign a module to a type, reference the module directly in your PreparedInputs:

    var PrepreparedInputs = new Map<UUID, TriggerBase.Serialised>(); 

And, as you did with TriggerEvent, export what types/variables you want to use outside a module:

// The interface was renamed to ITrigger in TriggerEvent.
import { Trigger as TriggerBase, ITrigger } from "./TriggerEvent"; 

export module ActionManager {
    export var Trigger = TriggerBase;

    export var EventsByID = new Map<UUID, ITrigger>();
    export var PrepreparedInputs = new Map<UUID, TriggerBase.Serialised>(); 
}

The answer by @Connor Low pointed me in the right direction by highlighting the right error, but this is the actual solution to the question.

From this github issue Imported modules from other files can be reexported using the import export Name = Value syntax.

So rewriting the example code, the solution is:

import { Subsection1 as Subsection1Base } from "./Subsection1";


export module GodManager {

    export import Subsection1 = Subsection1Base;

    var ObjByID = new Map<string, Subsection1.Interface>();
    export function DoThing(source) {
        ObjByID.set(source.id, Subsection1.DoInterfaceStuff(source.origin));
    }
}

And it all works!

Related