Ameen
Ameen

Reputation: 2586

Using modules in Meteor.js with Typescript

Folks, I'm trying to do something that I thought ought to be simple, but I must be doing something wrong. I'm trying to simply have a clear structure in my meteor application which uses Typescript.

Here are my requirements:

The setup that I have right now is this

In each .d.ts file I have

module MyModule {
     interface Bla {}
};

In each .ts file that defines a class I have:

module MyModule {
     export class MyBla implements Bla {};
}

All .d.ts files generated for classes are generated by tsc -d.

No .ts files are being included via ///<reference> rather only .d.ts files.

Now, when I run this, I get an error that MyModule is undefined:

/// <reference path="shared/include.d.ts"/>
/// <reference path="server/include.d.ts"/>
Meteor.startup(() => {
   var temp = new MyModule.ServerBook();
});

The error occurs right on MyModule.

What am I doing wrong? What should be the proper setup here?

Thanks!

Upvotes: 2

Views: 1591

Answers (5)

ncubica
ncubica

Reputation: 8475

Random idea, what about extend Meteor instead of Window.

Meteor.yournamespace = Meteor.yournamespace || {};
Meteor.yournamespace.myclass = new MyClass();

or

Meteor.yournamespace.MyClass = MyClass();

I think this is less invasive than go directly to the window object IMHO. my two cents.

now you can do Meteor.yournamespace.MyClass :P

--EDIT

Then you could create a meteor-extend.d.ts file and do something like:

/// <reference path="main.d.ts" />
declare module Meteor {
    var yournamespace: any;
}

Now you can remove the <any> before Meteor and Typescript will not complaint.

Upvotes: 0

tomitrescak
tomitrescak

Reputation: 1110

If you are not afraid of gulp build, I have prepared a typescript boilerplate project which allows you to comfortably use typescript from within your app, not depending on packages.

https://github.com/tomitrescak/meteor-boilerplate-typescript

Upvotes: 0

Vincent J
Vincent J

Reputation: 801

After lot of trial and errors, here are my findings so far :

Using typescript "module" keyword doesn't get well with Meteor. I think at the moment you cannot use it (or the workarounds are too complicated for me).

However, here is what you can do :

Let say that you have package A where you want to define a class ClassToExport which you want to make public.

class ClassToExport {
  getFoo(){
    return "foo";
  }
}

Please note that you can't write this.ClassToExport = ClassToExport and api.export('ClassToExport') or else ClassToExport won't be available in the global scope of package A, hence the need for a module/namespace for exporting your class, which we will see next.

Now, for the class to be available for the consumers of your package, you have to create a namespace, which will be the equivalent of the "module" typescript keyword for internal module.

So let's write :

declare var packageA; //so that the compiler doesn't complain about undeclared var
packageA = packageA || {}; //so that this namespace can be reused for the entire package
packageA.ClassToExport = ClassToExport; //the actual export

Now, don't forget to write api.export('packageA') in the package.js of package A

If you have a package B where you want to use ClassToExport, you write in package B:

var cte = new packageA.ClassToExport();

without forgetting to api.use package A in package B's package.js

If you don't want to write the namespace each time you use the class, you can also write var ClassToExport = packageA.ClassToExport; at the top of your using file.

If you need a global class for you package only, without exporting it, then you can do instead just :

this.ClassToExport = ClassToExport

and again don't write api.export('ClassToExport'), or it won't be available in the package anymore.

This way, i think the features (export/ import) of internal typescript modules are there.

Upvotes: 0

tomitrescak
tomitrescak

Reputation: 1110

I have dealt with this issue on my blog. I decided to use the evil eval command, since it gave me the easiest possibility of using modules till something more sophisticated appears.

File /lib/foo.ts is position in the subdirectory since it has to be loaded before Bar.

eval('var Hugo = (this.Hugo || (this.Hugo = {})'); // this will override the automatically emitted var Hugo and assigns it with globally defined Hugo module 

module Hugo {
  export class Foo {
    foo():string {
      return 'foo'
    }
  }
}

File /bar.ts

/// <reference path="lib/foo.ts"/>
eval('var Hugo = (this.Hugo || (this.Hugo = {})'); // this will override the automatically emitted var Hugo and assigns it with globally defined Hugo module 

module Hugo {
 export class Bar extends Foo {
    bar () : string {
      return 'bar';
    }
  }
}

File /test.ts

/// <reference path="lib/foo.ts"/>
/// <reference path="bar.ts"/>

var m = new Hugo.Bar();
console.log(m.bar());
console.log(m.foo());

As mentioned here, for classes, the solution is even simpler:

class ExportedClass {
    variable : int;
} 
this.ExportedClass = ExportedClass;

Upvotes: 3

Fenton
Fenton

Reputation: 250882

Definition files should use the declare keyword. You would normally get an error if you didn't use this keyword.

declare module MyModule {
     export interface Bla {}
}

And

declare module MyModule {
    export class MyBla implements Bla {

    }
}

It is also worth checking that the ServerBook class has the export keyword (just like MyBla in your examples).

Upvotes: 1

Related Questions