Vardule

Topics: General, Language Specification
Mar 5, 2014 at 7:45 AM
A lot of Javascript library (Facebook React for example) force us to create our objects trough some factory that hey provide:
declare module MyLibrary {
    interface Component<A> {
    }
    function createMyComponent<A>(): (a: A)=> Component<A>;
}
However using those library make the use of export = syntax nearly impossible :
class A {
}
var MyCompFactory = MyLibrary.createMyComponent<A>();//exported variable MyCompFactory has or is using private type A 
export = MyCompFactory; 
and if I try to use declaration merging:
var MyCompFactory = MyLibrary.createMyComponent<MyCompFactory.A>();
module MyCompFactory { // error: duplicate identier MyCompFactory
    export class A {
        
    } 
}
export = MyCompFactory; 
If we had something like vardule , this example would be resolvable.
Developer
Mar 7, 2014 at 1:13 AM
Does this do what you want?
module My {
    export class A { }

    export var MyCompFactory = MyLibrary.createMyComponent<A>();
}
export = My; 
Fundamentally there's no way to merge a module and a variable like you're proposing. A module is just a variable in JavaScript so all you'd be doing is overwriting the value of the first instance with the second one.
Mar 7, 2014 at 1:42 AM
Edited Mar 7, 2014 at 1:43 AM
When I think about it my main problem is the impossibility to reference private type in exported one, and all the restriction that cast on the export = syntax.

For your example, is it not the same as doing this ?
export class A { }

export var MyCompFactory = MyLibrary.createMyComponent<A>();
Fundamentally there's no way to merge a module and a variable like you're proposing. A module is just a variable in JavaScript so all you'd be doing is overwriting the value of the first instance with the second one.

If the example I have given in my first post :
var MyCompFactory = MyLibrary.createMyComponent<MyCompFactory.A>();
module MyCompFactory { // error: duplicate identier MyCompFactory
    export class A {
        
    } 
}
export = MyCompFactory; 
would translate to :
var MyCompFactory = MyLibrary.createMyComponent();
var MyCompFactory;
(function (MyCompFactory) {
    var A = (function () {
        function A() {
        }
        return A;
    })();
    MyCompFactory.A = A;
})(MyCompFactory || (MyCompFactory = {}));
Where would be the problem ? except if MyCompFactory was a primitive type but would not such case be easily caught by the compiler ?
In fact especially in this case MyCompFactory is a function so is it not equivalent to function with module merging ?
Developer
Mar 7, 2014 at 7:58 PM
I don't think it would be a good idea to allow variables and modules to merge. You'd be modifying whatever object happens to be stored in the variable (which may or may not be ok), but the modifications would disappear if another value is assigned to the variable. Would be a very odd construct.

I think you can just use a function/module combination in your scenario:
function MyCompFactory() {
    return MyLibrary.createMyComponent<MyCompFactory.A>();
}
module MyCompFactory {
    export class A {
        ...
    }
}
export = MyCompFactory;
Mar 8, 2014 at 11:37 AM
Edited Mar 8, 2014 at 7:26 PM
I don't think it would be a good idea to allow variables and modules to merge. You'd be modifying whatever object happens to be stored in the variable (which may or may not be ok), but the modifications would disappear if another value is assigned to the variable. Would be a very odd construct.

Yes you're right, I did not see those limitations.

Anyway as I said in my previous post my main problem is more due to the impossibility of referencing private type in exported one and all the restrictions that cast on the export = syntax. I just wanted vardule to paliate to these problems.

For your solution, unfortunately that still doesn't work.
Firstly the real createMyComponent has this definition :
function toReactComponent<A, C extends ReactComponent<any, any>>(
        definition: { 
            new(): C ;
            //just a little trick to make typescript infer valid type on generated factory
            prototype: { props: A } 
        }
    ): (props: A, ...children: any[]) => C;
}
and I want to be able to do something like that :
interface Props {
...
}
class Definition extends ReactTypescript.ReactComponentBase<Props, {}> {
...
}
var MyComponent = ReactTypescript.toReactComponent(Definition);
export = MyComponent;
secondly toReactComponent does complex computation that I do not want to repeat each time I want to create a new instance of MyComponent

so the only solution that I see would be :

var MyComponentFactory = ReactTypescript.toReactComponent(Definition);

function MyComponent(props:MyComponent.props, ...children: any[]): MyComponent.Definition {
    return MyComponentFactory.apply(undefined, arguments);
}

module MyComponent {
    interface Props {
    ...
    }
    class Definition extends ReactTypescript.ReactComponentBase<Props, {}> {
    ...
    }
}


export = MyComponent;
But that makes me lose all benefits of type inference.

Thanks for your answer.