Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use external modules within internal module #909

Closed
linvi opened this issue Oct 17, 2014 · 13 comments
Closed

Use external modules within internal module #909

linvi opened this issue Oct 17, 2014 · 13 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@linvi
Copy link

linvi commented Oct 17, 2014

I am trying to use external module within an internal module but Visual Studio won't let me do that.
As soon as I add an import x = require('xxx'); the internal module no longer consider the file to be part of the module and all exports are not referenced by the module anymore.

I have read the Module Documentation but I could not find the scenario of using external modules within an internal module.

As described in my post, I want to create a ViewModel module that will contain all the ViewModels of my application.

The application will use Knockout in order to insure the communication between the Views and the ViewModel and I therefore need to import the knockout module.

// HomePageViewModel.ts
import ko = require('knockout'); // When adding this line I can no longer access HomePageViewModel from the internal ViewModels module
module ViewModels {
    export class HomePageViewModel {
        constructor() {
            console.log('Creating HomePageViewModel');
        }
    }
}
// ViewModelFactories.ts
module OtherModule {
    export class ViewModelFactories {
        public static CreateHomePageViewModel(): ViewModels.HomePageViewModel {
            return new ViewModels.HomePageViewModel();
        }
    }
}

When importing the external module knockout, the internal module no longer exports the HomePageViewModel class.

Thank you for your help.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Oct 17, 2014
@RyanCavanaugh
Copy link
Member

I think the question was decently answered on StackOverflow. You can't be "half" internal module and "half" external module -- this is just a fact of how AMD module loading works. The solution is to either make your application 100% internal modules or 100% external modules.

@jednano
Copy link
Contributor

jednano commented Oct 17, 2014

I've struggled with this problem a lot myself, which is why I don't use ES6 modules. Maybe it makes more sense for a Node application. I'd love a better solution though.

@sebastian-lenz
Copy link

Actually the TypeScript compiler itself is "half" internal module and "half" external module. You are loading AMD modules in sys.ts while not being an AMD module. As there is no solution available currently, the TypeScript compiler itself doesn't use type checking here and falls back to any.

I think it should be possible to either use the import statement inside non AMD modules like in the example above or at least reference the typings of named modules like this:

var ko: typeof "knockout" = require('knockout');

@jednano
Copy link
Contributor

jednano commented Oct 26, 2014

@sebastian-lenz if you reference the DefinitelyTyped knockout.d.ts file at the top, you can:

///<reference path="typedefs/knockout.d.ts" />
import ko = require('knockout');

@alex6o
Copy link

alex6o commented Dec 21, 2014

@jedmao
I have the same problem when using mongoose within my modules ... i can import mongoose and reference the it with the definition files of definitelytyped, then mongoose types are resolved correctly but my internal references cannot be resolved anymore. so the solution for this problem is to use only external module definitions ?

@csnover
Copy link
Contributor

csnover commented Mar 8, 2015

@RyanCavanaugh For instantiated modules, this is true. But the compiler complains in non-instantiated modules, too, which means it is impossible to describe global types in an ambient declaration if those global types refer to a type that comes from some other external module:

// in foo/foo.d.ts
/// <reference path="../external/external.d.ts" />

declare module foo {
    // TS Error: Import declarations in an internal module cannot reference an external module.
    import request = require('external/request');

    interface RequestError extends Error {
        // …

        /**
         * The response object for the request.
         */
        response: request.IResponse;
    }

    // …
}

declare module 'foo/Bar' {
    // …
    class Bar {
        error: foo.RequestError;
        // …
    }
    export = Bar;
}

// …

The internal module foo could be written as an external module, but given the way other declaration files are authored, I think that would be considered an anti-pattern at this point.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 8, 2015

@csnover is it expected that the JavaScript molded by these definitions be accessed as both an internal module (e.g. include the js in a script tag) or required (either using an AMD loader or commonjs)?

one pattern that i have seen is to define all your typeings in an internal module, and then have an external module to expose them as well:

declare module foo {
     function f ();
     var x;
]
declare module "foo" {
    export = foo;
}

The main problem is that external modules and internal modules are really different, the shared syntax does not help here obviously. The compiler can understand external modules consuming internal modules, as they just represent global objects, but it can not understand what the opposite means in absence of a loader system.

Since your code is accessible both as a module, or addition to the global namespace, i assume your dependencies are as well (e.g. 'external/request'), if your dependencies expose an internal module (something along the lines i mentioned above) then that should solve the problem.

We have the same problem with typescript definitions, and the way we worked around it is by using two files one internal and one external, though we have no dependencies that we expose in the .d.ts.

@csnover
Copy link
Contributor

csnover commented Mar 8, 2015

@csnover is it expected that the JavaScript molded by these definitions be accessed as both an internal module (e.g. include the js in a script tag) or required (either using an AMD loader or commonjs)?

No, always required. The internal type foo is just for the type system, it’s never related to any real code. See https://github.com/theintern/intern/blob/typings/typings/leadfoot/leadfoot.d.ts#L5-L51 for a concrete example.

if your dependencies expose an internal module (something along the lines i mentioned above) then that should solve the problem.

Unfortunately I don’t think it’s safe to presume that the way the external dependencies are structured could be changed, as they are, well, external.

In this concrete example above, the dependencies come from well-structured modular code, and it would be wrong for that upstream project to move all these interfaces out of external modules.

(P.S. Sorry for not linking to the real examples before, they weren’t done at the time I wrote the post.)

@marcinporebski
Copy link

What I understand is there no option to create standalone node.js application consisting of internal modules? As using node.js requires importing external dependencies (like streams, events etc.) hence every module using these becomes external module. It's even more restrictive, extending this policy on non-module TS files, so that any file containing require can't be used as <reference>.
Correct me if I'm wrong, but this makes internal modules completely useless, and the only hope is in Suggestion: multi-file external modules #17.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 11, 2015

@csnover sorry for the late reply. I am a bit confused. so why do you need the internal module for your typings?

@csnover
Copy link
Contributor

csnover commented Mar 11, 2015

As a convenience for generic interfaces that are used in multiple places across an entire package but don’t emit code. The alternative is to create some a separate interfaces/typedefs module, which could be OK (and would improve portability), but just runs against how these things are typically modelled right now in DefinitelyTyped. I’m mostly OK moving to that model if that’s what everyone thinks is best, but I’m sure some people will complain about all the extra typing they have to do whenever they want to use one of these generic interfaces (most developers are incredibly lazy).

@mhegazy
Copy link
Contributor

mhegazy commented Mar 11, 2015

i see. thanks for the explanation. The problem is internal modules should have been called namespaces :). i do not think the two really mesh well just because of dependencies in external modules, and the open-ended nature of internal modules.

@mhegazy
Copy link
Contributor

mhegazy commented Jun 12, 2015

I do not think there is more actions here that are not already captured in other issues. please reopen a new issue if this is not the case.

@mhegazy mhegazy closed this as completed Jun 12, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

8 participants