2

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:

  • All interfaces are available in both client and server
  • Some class implementations are only available on the server
  • I don't want to rely on file load order for my application to work properly
  • I need my own module to not clash with global objects (such as the Position class for example)
  • I need to have one monolithic include file for server, one for both client and server and one for client (don't want to have 10s of includes on top of my files)

The setup that I have right now is this

  • server
    • server-book.ts
  • client
  • shared
    • collections.ts
  • definitions
    • server
      • include.d.ts (includes all .d.ts files in this folder)
      • server-book.d.ts (server specific implementation of book)
    • client
    • shared
      • include.d.ts (includes all .d.ts files here)
      • book.d.ts (book interface definition)
      • collections.d.ts

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!

Ameen
  • 2,576
  • 1
  • 14
  • 17

5 Answers5

3

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;
Community
  • 1
  • 1
tomitrescak
  • 1,072
  • 12
  • 22
1

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).

Fenton
  • 241,084
  • 71
  • 387
  • 401
0

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.

Vincent J
  • 761
  • 1
  • 5
  • 22
0

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

tomitrescak
  • 1,072
  • 12
  • 22
0

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.

ncubica
  • 8,169
  • 9
  • 54
  • 72