Declaring aliases for module/functions that returns modules

Topics: General, Language Specification
Jun 5, 2013 at 9:05 PM
So, here we go again with the "module is no longer a type" change.

This code won't compile in 0.9 beta:
declare function Sammy();

declare module Sammy {
    class Application {}
}

interface JQueryStatic {
    sammy: Sammy;
}
Giving error TS4022: Type reference cannot refer to container 'Sammy'.

if I add a declare class Sammy {} just to satisfy the reference to a type, it compiles.

But if I try to access the Application class through the $.sammy.Application, it throws an error:

error TS2094: The property 'Application' does not exist on value of type 'Sammy'.


It seems that creating the alias to a module is not working. The alias seems to reference only the class, not the function or the module.

A related issue seems to be declaring a "noConflict" method, where the method returns the module.
declare module Backbone {
    function noConflict() : Backbone;
}

Backbone.noConflict();
Gives error TS4022: Type reference cannot refer to container 'Backbone'

Adding a declare class Backbone {} now gives: error TS2000: Duplicate identifier 'Backbone'.

The funny thing is that adding an extra line: declare function Backbone(); makes this error go away, but then again, the return of the noConflict points to the class "Backbone", and not to the module.

Is this a known issue?

This thing about modules not being types anymore seems to be causing more issues than solving them. Was this rule enforced because ES6 has this behavior?

If so, it seems to me that another way to define this would be sub classes.

Something like:
declare class Foo {
    declare class Bar {
        declare class Inner {}
    }

    static noConflict() : Foo;
}

interface JQueryStatic {
    foo: Foo;
}
The only problem with this is that Foo shouldn't be a class, but only a container.

And this brings us back to the issue that containers in javascript are just plain objects and you can pass them around, point to them and return them.

Is there any solution to this problems that the developers didn't share yet?
Developer
Jun 6, 2013 at 2:42 AM
I suggest looking at section 10.5 Declaration Merging of the current language spec along with the chapter on ambient declarations.
It seems that creating the alias to a module is not working. The alias seems to reference only the class, not the function or the module.
You did not create an alias anywhere in your code. When you added 'declare class Sammy {}' what you did was use an ambient declaration tell the compiler that it should assume that there exists a class named Sammy with the specified shape (in this case, an empty object with no members). The spec explains in which situations you are allowed to have multiple declarations with the same name and how those declarations are merged if it is a valid combination (note that this feature was added fairly recently and there are likely to be bugs here).

For your Backbone example you want an interface to describe the shape of object that noConflict returns. For example, this works:
interface Backbone {
    foo(): void;
}

declare module Backbone {
    function noConflict(): Backbone;
}

var r = Backbone.noConflict();
r.foo();
This thing about modules not being types anymore seems to be causing more issues than solving them. Was this rule enforced because ES6 has this behavior?
If modules introduce types then they cause conflicts with other declarations of the same name which also introduce types (ex functions, classes, etc). This means there are a variety of common patterns in JavaScript which cannot easily be expressed in a type safe manner with TypeScript. For example, consider a function type where you would like to add additional properties to the function. In previous versions of TypeScript the only way to do this was like so:
function Foo() { return 1; }
(<any>Foo).x = 3;
var r1 = Foo.x // error
var r2 = (<any>Foo).x; // r2 is typed any, not number
It was an error to have a module named Foo and a function named Foo because they introduced conflicting type definitions. Today, you can use a module by the same name to augment the function Foo with additional values:
function Foo() { return 1; }
module Foo { export var x = 2; }
var r1 = Foo.x; // no error, x is in the completion list for Foo and is of type number
var r2 = Foo();
For a similar real world example, consider how JQuery uses $ to represent a callable function which also has other members of its own which can be called.
And this brings us back to the issue that containers in javascript are just plain objects and you can pass them around, point to them and return them.
A JavaScript module only contains values, not type definitions. A TypeScript module can contain one or both. This creates meaningful differences between the two in some situations. There are other TypeScript constructs you can use to pass around only values, namely interfaces, as I did with your Backbone example. Does that make more sense?
Jun 6, 2013 at 12:04 PM
You did not create an alias anywhere in your code. When you added 'declare class Sammy {}' what you did was use an ambient declaration tell the compiler that it should assume that there exists a class named Sammy...
The alias was not the class. The alias is the $.sammy. See:
function Sammy() { }

declare module Sammy {
    class Application {}
}

interface JQueryStatic {
    sammy: Sammy;    <--- this is the alias I referred to.
}
The identifier "$.sammy" should be exatcly the same as the identifier "Sammy". It doesn't matter if its a class, a module, a function or both. We just need the word "sammy" be the same as "Sammy". The reason is that the code for this in javascript is quite simple: $.sammy is a reference to the Sammy object.
var Sammy = function() {}
Sammy.Application = function() {}
$.sammy = Sammy;
For your Backbone example you want an interface to describe the shape of object that noConflict returns.
Yes, it is possible to achieve this using interfaces, just like JQuery is declared. The only problem is that you need a "Static" version of the declarations.
Please correct me if I'm overcomplicating this, but this was the only way I could make this work:
declare function Sammy(): Sammy.Application;
declare function Sammy(a, b, c): Sammy.Application;

declare module Sammy {
    class Object { }
    class Application { }
    
    function noConflict(): SammyStatic;
}

interface SammyStatic {
    (): Sammy.Application;
    (a, b, c): Sammy.Application;

    Object: Sammy.Object;
    Application: Sammy.Application;
    
    noConflict(): SammyStatic;
}

interface JQueryStatic {
    sammy: SammyStatic;
}

declare var $: JQueryStatic;

// sample code:
Sammy();
Sammy(1, 2, 3);
new Sammy.Application();

new $.sammy.Application();

var S = Sammy.noConflict();
S();
S(1, 2, 3);
new S.Application();
It seems relly overcomplicatedto have that interface there. All module contents and functions need to be redeclared, just because I cannot say that $.sammy is a reference to the object Sammy.
If there is a way to do this without duplicating everything in the SammyStatic interface, can you please show an example that runs that sample code?
A JavaScript module only contains values, not type definitions. A TypeScript module can contain one or both. This creates meaningful differences between the two in some situations. There are other TypeScript constructs you can use to pass around only values, namely interfaces, as I did with your Backbone example. Does that make more sense?
I guess you made my point. It seems that there are ways to do this, they just make it a hell to maintain the definitions, add a lot of extra syntax and don't map to what is really happening.

Looking at modules as containers for classes and functions, modules are just plain object values.
module Foo {}
///compiles basically to
var Foo = {}
I don't intend to discuss how the compiler should map them, if they should be types or not. But they should behave a little bit like them in some cases.

All of this complication goes back to the fact that you can't just say:
module M {}
interface Foo {
    m: M;
}
function m() : M;
Am I saying that modules should be types? No. I'm just stating that the above code should work because what is really happenning is this:
var M = {};
var Foo = {
    m: M
}
function m() { return M; }
Does it make sense now?
Developer
Jun 6, 2013 at 5:20 PM
Here is how you'd write your example:
declare function Sammy(): Sammy.Application;
declare function Sammy(a, b, c): Sammy.Application;

declare module Sammy {
    class Application { }
    function noConflict(): SammyStatic;
}

interface SammyStatic {
    (): Sammy.Application;
    (a, b, c): Sammy.Application;
    Application: new () => Sammy.Application;
    noConflict(): SammyStatic;
}

interface JQueryStatic {
    sammy: SammyStatic;
}

declare var $: JQueryStatic;
With these declarations, all of your sample code works.

The primary reason a module can no longer be used as a type is that it would make it impossible to form module/class combos (like the module/function combos you are using here, but with a class instead). Module/class combos are particularly useful when you want to declare nested classes, a pattern we've seen in several existing JavaScript frameworks. Consider this example:
class Geometry {
    distanceTo(other: Geometry): number { ... }
}
module Geometry {
    export class Curve extends Geometry { }
    export class Point extends Geometry { }
    export class Polygon extends Geometry { }
}
var g: Geometry = new Geometry();
var p: Geometry.Point = new Geometry.Point();
Here, Geometry has three different meanings: It is a constructor function object (with nested constructor function properties), it is a type (the instance type of the class), and it is a namespace (containing other types). The different contexts in which Geometry is used determines the meaning. In an expression it refers to the constructor function object (with its nested constructor function properties), in a type position it refers to the Geometry class instance type, and on the left hand side in a dotted type name it refers to the namespace.

Another way to think of these declarations is:
interface Geometry {
    distanceTo(other: Geometry): number { ... }
}
module Geometry {
    export interface Curve extends Geometry { }
    export interface Point extends Geometry { }
    export interface Polygon extends Geometry { }
}
var Geometry: {
    new (): Geometry;
    Point: { new (): Geometry.Point; }
    Curve: { new (): Geometry.Curve; }
    Polygon: { new (): Geometry.Polygon; }
};
As you can see, the Geometry constructor function (the 'var Geometry' above) is of an anonymous type. Previously, a module declaration would have given the type name 'Geometry' to that type, but that would conflict with the instance type of the Geometry class and make the nested class construct impossible to express.

Hopefully this clears it up. The key thing to remember is that names can have as many as three different meanings in TypeScript: As a member (variable), as a type, and as a namespace (container of types) and that each of the different constructs in the language introduce combinations of these.
Jun 6, 2013 at 5:38 PM
ahejlsberg wrote:
As you can see, the Geometry constructor function (the 'var Geometry' above) is of an anonymous type. Previously, a module declaration would have given the type name 'Geometry' to that type, but that would conflict with the instance type of the Geometry class and make the nested class construct impossible to express.

Hopefully this clears it up. The key thing to remember is that names can have as many as three different meanings in TypeScript: As a member (variable), as a type, and as a namespace (container of types) and that each of the different constructs in the language introduce combinations of these.
So there is an implicit, named interface for a class, and an implicit, unnamed interface for a module, to avoid ambiguities between implicit interfaces. Wouldn't an alternative be to allow explicit disambiguation of interfaces? Something like
declare var Geometry : module Geometry; // refer to the implicit module interface
That would allow to name the implicit module interfaces where needed.
Developer
Jun 6, 2013 at 5:51 PM
Sure, you could imagine many possible new features to name and reference the type of a module, but at the end of the day they would have to justify the additional complexity they introduce. It's not clear that it is that important to have a name for the type of a module--for example, we don't introduce names for the types of functions either. And interfaces already provide a perfectly fine way of introducing named types. If we can do it with one less feature, I think we're in a better place.
Jun 6, 2013 at 6:40 PM
English is not my primary language, so I guess I'm not being clear on the issue I'm seeing and we are talking apples and oranges here.

You both insist on showing why modules shouldn't be types, and why you made the change, and how this allows new scenarios. I really have nothing against that.

I'm just pointing out that because of this change, one of the most common patterns in javascript today can only be mapped in typescript by duplicating a lot of code, and there is obviously something wrong about that.

And it just puzzles me that this doesn't bother you at all.

There is really nothing on this problem that implies that the only way to avoid this is to transform modules into types.
Developer
Jun 9, 2013 at 12:45 AM
We're currently contemplating a feature that would be useful in this and several other scenarios: Allowing the typeof operator to be used in a type to extract the type of an expression. For example:
var a = { x: 10, y: 20 };
var b: typeof a;
This would declare b to have the same type as a (i.e. { x: number; y: number; }). The feature could be used to extract anonymous types that are inferred and/or generated in many different situations in TypeScript, including the type of a function, the type of the static side of a class, and the type of a module.

Returning to the previous examples in this thread, with this feature you'd be able to write:
declare function Sammy(): Sammy.Application;
declare function Sammy(a, b, c): Sammy.Application;

declare module Sammy {
    class Application { }
    function noConflict(): typeof Sammy;
}

interface JQueryStatic {
    sammy: typeof Sammy;
}

declare var $: JQueryStatic;
The kinds of expressions you'd be able to apply typeof to would likely be limited to just simple or qualified names (dotted names).
Jun 10, 2013 at 5:25 PM
Hi Anders,

Wouldn't this conflict with the regular use of typeof?
module Foo {
   function noConflict() : typeof Foo {
       return typeof Foo;
   }
}
In this case, should this be compiled to "return typeof Foo" or to "return Foo" ?

I think the idea of a different syntax to avoid ambiguity is cool, but would be nice to have something that can be used to create the actual code, not only the declaration, if that was the idea.
Developer
Jun 10, 2013 at 6:28 PM
The proposed feature is exclusively about use of typeof in a type. The meaning of typeof in an expression wouldn't change. So, you wouldn't actually use the operator in the return statement:
module Foo {
   function noConflict(): typeof Foo {
       return Foo;
   }
}
Think of it this way: In your example there is no type 'Foo', but there is a variable (module) named 'Foo'. The typeof operator allows you to obtain the nameless type of that variable.
Jun 10, 2013 at 7:11 PM
Ok,, makes sense.

But now I got a question/proposal. Can't this be implicitly resolved at the AST level when the intention clear?

What I mean is:
module Foo {
   function noConflict(): Foo {
       return Foo;
   }
}
When this code gets compiled, the parser will identify the return of "noConflict" as Foo, and that Foo is not an actual type. If that is not a type, but there is an identifier called "Foo", what we intended was the "nameless type of the variable", so the AST implicitly converts Foo to "typeof Foo".

This could work as well for functions and parameters, so
function Mapper(key: string, value: number) {}

// and use the Mapper as a type implictly converted to typeof Mapper
function map(fn: Mapper) { ... }
If there is ambiguity, like an actual class called Mapper, the compiler can always choose the real type and you would be required to use "typeof Mapper" if that is what you intended.

Wouldn't this cover most cases where this is needed while making the code cleaner?
Jun 19, 2013 at 1:37 PM
Hi,

Was this typeof <module> removed before 0.9 alpha was released or was it changed to something else?

It was working a couple days back, but it is not compiling anymore on the released 0.9 version or in develop branch, giving an error:
declare module Foo {
   function noConflict(): typeof Foo;
}
test.ts(2,27): error TS1006: Identifier expected; 'typeof' is a keyword.
Developer
Jun 19, 2013 at 4:38 PM
We only started implementing the new use of the typeof keyword.

I'm not sure what you saw a couple of days ago, but stay tuned for a complete implementation in the next couple of weeks.
Aug 21, 2013 at 11:45 AM
Hi,

The 'typeof' seems working/compiling for me for now. So when I have the following:
//backbone.d.ts
 declare module Backbone{
    function noConflict: : typeof Backbone;
}

//CustomModule.ts
var ncBackbone = Backbone.noConflict();
ncBackbone works properly and has all the classes, functions, properties, etc.. However, when I try the following:
/// <reference path="../backbone.d.ts" />

var ncBackbone = Backbone.noConflict();
module CustomModule{
  export class CustomClass extends ncBackbone.Collection{
     //Code
}
}
It gives an error, underlining ncBackbone: Could not find symbol 'ncBackbone'

Am I missing something?

Thanks
Developer
Aug 21, 2013 at 1:58 PM
The problem is that 'ncBackbone' isn't itself a module, but just a regular variable, so it isn't possible to use it in a type name position like the 'extends' clause. The unhelpful error message will be more meaningful in the next release of the compiler (the fix is already in the 'develop' branch).
Aug 21, 2013 at 2:50 PM
Thanks.

So, how do we handle such situations, if the expected behavior is to be able to use the new 'ncBackbone' to extend one of it's classes?

If I use the following, that would refer to the older/other version of Backbone:
/// <reference path="../backbone.d.ts" />

      var ncBackbone = Backbone.noConflict();
      module CustomModule{
            export class CustomClass extends Backbone.Collection{
                //Code
       }
    }
OR should I be creating another module for ncBackbone, which is a replica of the Backbone module? Then, is there a way to assign ncBackbone module to the noConflict()?

I can imagine running into the same problem, in case someone wants to extend one of the Jquery classes in a similar way.
Thanks, again.
Developer
Aug 21, 2013 at 5:44 PM
Ultimately it comes down to being able to use an arbitrary expression (of a constructor function type) in the 'extends' clause. This is something we'd like to allow, but that feature won't make it for 1.0.

Meanwhile, for your particular scenario, I think it would all work out if you move the call to 'Backbone.noConflict()' to after your class declarations. That way, when the JavaScript generated for the class declarations executes at initialization time, it will see the most recently initialized Backbone instance and capture the constructor functions from that instance.
Aug 22, 2013 at 2:15 PM
Thank you. It works as you said. And wherever we want to use it as a variable, we use ncBackbone, instead.

Thanks.