Using Import within a function closure.

Topics: General
Sep 25, 2013 at 10:46 PM
Searched for ages for this, but I'm maybe missing something simple. :)

Using say RequireJS and the modules AMD inside typescript it's very easy to use, but I have one problem. Is there any reason the import/require cannot be used inside a function?.

The reason I ask is because I might say have a function that's called rarely, maybe controlled by some external event etc. But because I can only put the require call at the begging of the file and not inside the function, the JS is always going to be loaded.

As an example..
import rareProcs = require('rareProcs'); //don't want it here.

export function doSomething(doRarely:boolean) {
  if(doRarely) {
     //import rareProcs = require('rareProcs'); 
     //The above uncommented gives <- ERROR: Unexpected token; 'statement expected'
     rareProcs.doSomethingNotOften();
  }
}
If the second commented "import rareProcs" could be done, late loading of rarely used functions/classes etc could be loaded on-demand.

It would also help, say with single Page AJax style websites, so that associated Javascript for pages can be loaded on-demand.
Oct 2, 2013 at 12:06 PM
Ok, no response. So I assume it's not possible, and not on the roadmap.

Personally this make's TypeScript not usable for me. I'm a big fan of late loading!!.
Using TypeScript and AMD loading don't really work together. I see no point in using an AMD loader, if on page load all modules implicitly get loaded.

But, I will say thanks to the TypeScript Team, the one thing TypeScript did do for me is make the transition from a OOP language to a Prototype one much easer. After learning more about pure Javascript and prototype I believe in it's pure form it's already a beautiful language anyway.
Developer
Oct 2, 2013 at 1:58 PM
Here is what you would need to do:
/// <reference path="require.d.ts"/>

import _rareProcs = require('rareProcs');

export function doSomething(doRarely: boolean) {
    if (doRarely) {
        require(["rareProcs"], (rareProcs: typeof _rareProcs) => {
            rareProcs.doSomethingNotOften();
        });
    }
}
When an imported external module is referenced only in type positions the compiler optimizes away the dependency it would otherwise generate. So, in the example above, "rareProcs" is not included in the dependency array passed to the 'define' function. You can then manually load the module using your AMD loader's 'require' function, but since this loading will happen asynchronously so you need to provide the rest of your function as a callback. Also note that you can use the 'typeof' operator to strongly type the module object for the dynamically loaded module.

BTW, the "require.d.ts" file I reference above can be found on Definitely Typed. Or you can just do a 'declare var require: any' and forego strong typing.
Oct 3, 2013 at 9:34 AM
Hi ahejsberg,

Thanks for your response. The main problem here is that were basically hacking the import directive so that we can get Type information, so it doesn't really give you anything more than just using the /// <reference> directive. The other problem of course is that we also need to Typecast.

On the other hand if the import directive could have been extended to handle closures, rather than a top level directive, my example without comments would look very clean.
export function doSomething(doRarely: boolean) {
    if (doRarely) {
        import rareProcs = require('rareProcs'); 
        rareProcs.doSomethingNotOften();
    }
}
In the above there is no need for Typecasting, and no ///<reference> tags or import hacks that the compiler would need to optimize away.

Saying all this, I believe I know the reason why imports inside closures are currently not allowed. Looking at my code, you would think the code is running linear, when in fact it's Async. So if I altered my code too ->
export function doSomething(doRarely: boolean) {
    if (doRarely) {
        import rareProcs = require('rareProcs'); 
        rareProcs.doSomethingNotOften();
    }
    console.log('Do something done');
}
The console "Do something done' would most likely get fired before the doSomethingNotOften, and this is likely not what the programmer intended. To handle this correctly the Typescript compiler would have to be very clever.

eg, it would require compiling too something like ->
    export.doSomething = function(doRarely) {
        function _outsideIf() { console.log('Do something done'); }
        if(doRarely) {
            require(['rareProcs'],function(rareProcs) {
                rareProcs.doSomethingNotOften();
                _outsideIf();
            }
        } else _outsideIf();
    }
But even then, the doSomething has now potentially now become Async,.. Oh Heck!!!
One solution would maybe if the TypeScript compiler was promise based, but that I think would be a massive undertaking. Or maybe the Callee end of doSomething get's wrapped too, but I can see how this would turn the javascript code into spaghetti.
Oct 3, 2013 at 2:20 PM
@KpjComp,

The import is not buying you much in this situation. As you mentioned, another solution is to simply <reference> include the necessary types.

It shouldn't be too difficult to structure something like the following, with recourse to one or the other promise library:
/// <reference "MyAmdLoader.d.ts" />
/// <reference "MyPromiseLib.d.ts" />

function doSomethingRarely(doRarely) : MyPromiseLib.Promise<void> {
        if(doRarely) {
            
            return MyAmdLoader
                .load<RareProcs>("rareProcs")
                .then(rareProcs => {
                
                rareProcs.doSomethingNotOften();
            });
        }
        else {
            
             var promise =  MyPromiseLib.create();          
             console.log('Do something done');
             promise.resolve();
             return promise;             
        }
}
It is unavoidable that doSomethingRarely is an asynchronous function, hence it must return a promise. The AMD loader is also clearly asynchronous. So IMO all that is clearly spelled out in the code for anyone reading it to understand without confusion.

In a future version, TypeScript may well provide some shorthand syntax (sugar) for the code above, but even as it stands it's not really spaghetti. (Damn never knew there was an "h" in spaggetti. Thanks spell checker!)
Oct 3, 2013 at 4:33 PM
Hi @nabog,

My quote on promises, was for the compiler to implement them automatically. IOW: to make what looks like linear code behave like linear code. If typescript had the ability to flag a function as been a potential Async call, typescript could then maybe automatically wrap this inside promise calls. I think that makes sense, well in my head it does.. :) Of course this idea is unlikely to happen, as Typescript as best it can adheres to the ECMAScript standard, and I assume there is no standard for this. But I'm not sure the current import is ECMAScript standard either,. eg. the current idea for import is something like -> import $ from "jquery"; And also ECMAScript seems to have late loading with the System.import & system.Load directive,. So I assume typescript will hopefully be implementing this.

Your code is of course implementing promises using Typescript.

Although for this simple example the code is indeed not spaghetti, compared to the 7 line typescript example I provided it it does look a little bit more, it's also got the same issue's as before, your still having to typecast, IMO: avoiding typecast's would be nice.

A little note on your example, from what I can tell your returning different promises depending on if doRarely is called. Would it not be better to always return the same type of promise?, this way you could send some sort of status back via the resolve..

eg. (oh, just sussed how you get formatted code)
/// <reference "MyAmdLoader.d.ts" />
/// <reference "MyPromiseLib.d.ts" />

function doSomethingRarely(doRarely) : MyPromiseLib.Promise<void> {
        var promise =  MyPromiseLib.create();          
        if(doRarely) {            
            MyAmdLoader
                .load<RareProcs>("rareProcs")
                .then(rareProcs => {                
                rareProcs.doSomethingNotOften();
                promise.resolve('did something once in a blue moon');
            });
        }
        else {
             console.log('Do something done');
             promise.resolve('not this time, maybe next time');
        }
        return promise;             
}
Oct 3, 2013 at 4:58 PM
Hi, @KpjComp,

I was aware that you wanted the compiler to implement the promise. My point was that one could accomplish this even today relatively painlessly with an external library like Q.

There is also an issue out for TypeScript to implement async/await a la C#. The reason this was not implemented initially, as I understand it, is that it clashes with another TypeScript goal: that of generating "clean" JavaScript output. Wrapping up asynchronous code is likely to lead to some spaghetti as you note.

I don't understand by what you mean when you say "your still having to typecast, IMO: avoiding typecast's would be nice.".

Finally, in my code I am not returning different promises. According to the promise spec then returns a promise whose resolved value is the return value from the on fulfilled callback. Since I'm not returning anything from on fulfilled the return value is undefined or void in the TypeScript world.
        .then(rareProcs => {
                
                rareProcs.doSomethingNotOften();
            // void
            });
My second promise is also resolved without a value promise.resolve(). Hence they are equivalent. In your code there is an extra overhead when the doRarely block is executed, because an additional promise is created.

A very minor optimisation of course.
Oct 3, 2013 at 9:43 PM
Hi, @nabog..
I don't understand by what you mean when you say "your still having to typecast, IMO: avoiding typecast's would be nice.".
Basically this bit -> return MyAmdLoader.load<RareProcs>("rareProcs") <- <RareProcs> is a typecast, when using pure imports in typescript this is not required.
Finally, in my code I am not returning different promises.
Sorry, yes. They both return a promise, and a promise is a promise after all. It was if later on when you wanted to implement something in the resolve, for a return value, it's the resolve I see as the return and not the promise. But then you can always re-factor as needed. I suppose for me having the creation of the promise at the start of the function and returning this promise just seems to define the scope of the function to be more obvious. In your example were both return void, it's of course not relevant but the example I showed returning a string for status it is.
In your code there is an extra overhead when the doRarely block is executed
Yes, but it's returning something extra in the resolve, that the "then" promise wouldn't be doing. Like you say it's a minor optimization, as it is a doRarely after all. :)

Anyway, I think we've gone slightly off track here. :)
My main concern was with lazy loading, and if the Typescript team could implement ECMAScript 6 System.import & System.load that would be great, they both imply Async so no confusion either. Because Typescript would understand System.import, in theory we also have type safety again without typecasting.