Merge pull request #2786 from fullflavedave/master

Updated meteor.d.ts and meteor-tests.ts for Meteor 0.9.1.
This commit is contained in:
Masahiro Wakame
2014-09-15 22:49:04 +09:00
3 changed files with 738 additions and 764 deletions
+72 -77
View File
@@ -1,106 +1,101 @@
#Meteor Type Definitions
-------------------------
# Meteor Type Definitions
These definitions are now deprecated. Although they will still work up to Meteor version 0.6.5.1 (and possibly much later), the better way to develop a Meteor app with TypeScript is by installing the [typescript-libs](https://atmosphere.meteor.com/package/typescript-libs) Meteor smart package.
These are the definitions for version 0.9.1 of Meteor. Although these definitions can be downloaded separately for use, the recommended way to use these
definitions in a Meteor application is by installing the [typescript-libs](https://atmosphere.meteor.com/package/typescript-libs) Meteor smart package.
The smart package contains TypeScript definitions forMeteor, common third-party libraries (e.g. jquery, underscore, d3 etc.), and common smart packages
(e.g. iron-router).
From within any Meteor application that is version 0.9.0 or later, install this package in the standard manner:
$ meteor add typescript-libs
You first need to have (Meteorite)[http://oortcloud.github.io/meteorite/] version and smart package manager. If you don't have it, install it with this command:
- `npm install -g meteorite`
## Usage Overview
For most applications, there are 4 specific steps you will have to take to write your Meteor application in TypeScript using this package:
1. [Reference the definitions] (#usage-type-definition-references)
2. [Declare functions for Templates in a special way] (#usage-templates)
3. [Declare Collections in a special way] (#usage-collections)
4. [Create custom definitions for code you write] (#usage-creating-definitions)
5. [Transpile your .ts files into .js files] (#usage-transpilation)
Once you have installed Meteorite, you can easily install the smart package with this command:
- `mrt install typescript-libs`
- I also recommend installing the Meteor typescript compiler: `mrt install typescript-compiler`
## Usage: Type Definition References
Within any TypeScript file, you can reference the Meteor definition file with this line:
///<reference path="/path/to/packages/typescript-libs/meteor.d.ts" />
The smart packages and documentation for them can be found on [Atmosphere](https://atmosphere.meteor.com):
- [typescript-libs](https://atmosphere.meteor.com/package/typescript-libs)
- [typescript-compiler](https://atmosphere.meteor.com/package/typescript-compiler)
## Usage: Templates
When specifying template functions, you will need to use "bracket notation" instead of "dot notation":
Template['myTemplateName']['rendered'] = function ( ) { ... }
Template['myTemplateName']['helpers']({
foo: function () {
return Session.get("foo");
}
});
Template['myTemplateName']['foo'] = function () {
return Session.get("foo");
};
Using the typescript-libs smart package eliminates the need for the Template and Collections steps listed below, although there
are several new modifications necessary to use typescript-libs (e.g. calling Template['yourTemplate']['helpers'], creating Data Objects).
For "dot" notation, TypeScript requires properties be specified on a variable (but not for bracket notation), and it will throw an error saying "myTemplateName"
does not exist on Template.
Kudos to [Olivier Refalo](https://github.com/orefalo) for developing the smart packages quicker and better than me!
--------------------------
## Usage: Collections
The majority of extra work required to use TypeScript with Meteor is creating and maintaining the collection interfaces. However, doing so also provides the
additional benefit of succinctly documenting collection schema definitions (that are actually enforced).
To define collections, you will need to create an interface representing the collection, and then declare a Collection type variable with that interface type (as a generic):
interface JobDAO {
_id?: string;
name: string;
status?: string;
queuedAt?: string;
}
declare var Jobs: Meteor.Collection<JobDAO>;
Jobs = new Meteor.Collection<JobDAO>('jobs');
#Overview
Finally, any TypeScript file using collections will need to contain a reference at the top pointing to the collection definitions:
In order to effectively write a Meteor app with TypeScript, you will probably need to do these things:
- Reference the Meteor type definitions file (meteor.d.ts)
- Create a Template definition file
- Create Collections within a module or modules
/// <reference path="../packages/typescript-libs/meteor.d.ts"/>
/// <reference path="../packages/typescript-libs/underscore.d.ts"/>
/// <reference path="models/models.ts"/>
##Referencing Meteor type definitions in your app
- Place the meteor.d.ts file in a directory (maybe `<app root dir>/lib/typescript`)
- Add `/// <reference path='../lib/typescript/meteor.d.ts'/>` to the top of any TypeScript file
This will make these typed Meteor variables/objects available across your application:
- Meteor
- Session
- Deps
- Accounts
- Match
- Computation
- Dependency
- EJSON
- HTTO
- Email
- Assets
- DPP
*Please note that the Template variable is not automatically available. You need to follow the instructions below to use the Template variable.*
If you choose to define collections (using the code above) in a separate file (e.g. collections/models/models.ts) and then create a separate file per collection
with the methods and permissions for that collection (e.g. collections/jobs.ts), the collection definitions should be one directory deeper than the collection
method/permission declarations so that Meteor can find the variable declarations before use. (e.g. collections/models/models.ts).
##Defining Templates
In order to call `Template.yourTemplateName.method`, you will need to create a simple TypeScript definition file that declares a Template variable containing a list of template view-models/managers of type IMeteorViewModel (or IMeteorManager, which is the same as IMeteorViewModel). A good place for this definition could be `<app root dir>/client/views/view-model-types.d.ts`. Here is an example of that file:
/// <reference path='../../lib/typescript/meteor.d.ts'/>
declare var Template: {
newPosts: IMeteorViewModel;
bestPosts: IMeteorViewModel;
postsList: IMeteorViewModel;
comment: IMeteorViewModel;
commentSubmit: IMeteorViewModel;
notifications: IMeteorViewModel;
postPage: IMeteorViewModel;
postEdit: IMeteorViewModel;
postItem: IMeteorViewModel;
postNew: IMeteorViewModel;
header: IMeteorViewModel;
}
After you create this file, you may access the Template variable by declaring something similar to `/// <reference path='../view-model-types.d.ts'/>` at the top of any TypeScript file containing references to Template. Something like `Template.postsList.helpers()` would then transpile successfully (and also have the benefits of typing).
## Usage: Creating Definitions
Here is a guide to creating definitions: <http://www.typescriptlang.org/Handbook#writing-dts-files>
##Defining Collections
In TypeScript, global variables are not allowed, and in a Meteor app, creating a local variable (using `var <varName>`) limits a variable's scope to the file. However, you will probably want to define variables, such as collections, that can be used across multiple files. In the case of collections, one way to work around these limitations is to wrap the definitions of all collections within a module, and then make the module globally accessible. Here is an example (collections/models.ts):
module Models {
export var Posts = new Meteor.Collection('posts');
export var Comments = new Meteor.Collection('comments');
export var Notifications = new Meteor.Collection('notifications');
}
## Usage: Transpilation
WebStorm is good TypeScript-aware editor. It can automatically transpile your TypeScript code into JavaScript every time you save a file. To enable this
feature in WebStorm on OSX, first install the TypeScript transpiler on your system:
this.Models = Models;
$ [sudo -H] npm install -g typescript
You can then access the Posts collection by placing something similar to `/// <reference path='../../../collections/models.ts'/>` at the top of a TypeScript file. The code within the file would look something like this:
Then, within WebStorm, go to Preferences -> File Watchers -> "+" symbol and add TypeScript.
Models.Posts.findOne(Session.get('currentPostId'));
For organizational purposes, any additional code related to each Collection can be placed in a separate file per each collection. Alternatively, you could wrap each collection in its own module (e.g. PostsModel for posts, CommentsModel for comments).
If you are not using a TypeScript-aware editor, you can transpile the files using the [Meteor Typescript Compiler](https://github.com/orefalo/meteor-typescript-compiler).
##Reference app
Listed below is a simple Meteor reference application created with TypeScript is listed below. It is based on the Microscope reference app in [Discover Meteor](http://www.discovermeteor.com/ "http://www.discovermeteor.com/").
- Sample Site: <http://microscopic-typescript.meteor.com/>
- Code (TypeScript and transpiled JS): <https://github.com/fullflavedave/MicroscopicTypeScript>
## Example/Reference Projects
* [TypeScript demos](https://github.com/orefalo/meteor-typescript-demos)
+63 -110
View File
@@ -10,16 +10,19 @@
/*********************************** Begin setup for tests ******************************/
// A developer must declare a var Template like this in a separate file to use this TypeScript type definition file
interface ITemplate {
adminDashboard: IMeteorViewModel;
chat: IMeteorViewModel;
}
declare var Template: ITemplate;
//interface ITemplate {
// adminDashboard: Meteor.Template;
// chat: Meteor.Template;
//}
//declare var Template: ITemplate;
var Rooms = new Meteor.Collection('rooms');
var Messages = new Meteor.Collection('messages');
var Monkeys = new Meteor.Collection('monkeys');
var Rooms = new Mongo.Collection('rooms');
var Messages = new Mongo.Collection('messages');
var Monkeys = new Mongo.Collection('monkeys');
var x = new Mongo.Collection('x');
var y = new Mongo.Collection('y');
var check = function(str1, str2) {};
/********************************** End setup for tests *********************************/
@@ -88,9 +91,9 @@ Meteor.publish("counts-by-room", function (roomId) {
});
});
var Counts = new Meteor.Collection("counts");
var Counts = new Mongo.Collection("counts");
Deps.autorun(function () {
Tracker.autorun(function () {
Meteor.subscribe("counts-by-room", Session.get("roomId"));
});
@@ -106,7 +109,7 @@ Meteor.subscribe("allplayers");
/**
* Also from Meteor.subscribe section
*/
Deps.autorun(function () {
Tracker.autorun(function () {
Meteor.subscribe("chat", {room: Session.get("current-room")});
Meteor.subscribe("privateMessages");
});
@@ -138,11 +141,11 @@ Meteor.call('foo', 1, 2, function (error, result) {} );
var result = Meteor.call('foo', 1, 2);
/**
* From Collections, Meteor.Collection section
* From Collections, Mongo.Collection section
*/
// DA: I added the "var" keyword in there
var Chatrooms = new Meteor.Collection("chatrooms");
Messages = new Meteor.Collection("messages");
var Chatrooms = new Mongo.Collection("chatrooms");
Messages = new Mongo.Collection("messages");
var myMessages = Messages.find({userId: Session.get('myUserId')}).fetch();
@@ -150,7 +153,7 @@ Messages.insert({text: "Hello, world!"});
Messages.update(myMessages[0]._id, {$set: {important: true}});
var Posts = new Meteor.Collection("posts");
var Posts = new Mongo.Collection("posts");
Posts.insert({title: "Hello world", body: "First post"});
// Couldn't find assert() in the meteor docs
@@ -160,7 +163,7 @@ Posts.insert({title: "Hello world", body: "First post"});
* Todo: couldn't figure out how to make this next line work with Typescript
* since there is already a Collection constructor with a different signature
*
var Scratchpad = new Meteor.Collection;
var Scratchpad = new Mongo.Collection;
for (var i = 0; i < 10; i++)
Scratchpad.insert({number: i * 2});
assert(Scratchpad.find({number: {$lt: 9}}).count() === 5);
@@ -179,7 +182,7 @@ Animal.prototype = {
// Define a Collection that uses Animal as its document
var Animals = new Meteor.Collection("Animals", {
var Animals = new Mongo.Collection("Animals", {
transform: function (doc) { return new Animal(doc); }
});
@@ -191,8 +194,8 @@ Animals.findOne({name: "raptor"}).makeNoise(); // prints "roar"
* From Collections, Collection.insert section
*/
// DA: I added the variable declaration statements to make this work
var Lists = new Meteor.Collection('Lists');
var Items = new Meteor.Collection('Lists');
var Lists = new Mongo.Collection('Lists');
var Items = new Mongo.Collection('Lists');
var groceriesId = Lists.insert({name: "Groceries"});
Items.insert({list: groceriesId, name: "Watercress"});
@@ -201,9 +204,9 @@ Items.insert({list: groceriesId, name: "Persimmons"});
/**
* From Collections, collection.update section
*/
var Players = new Meteor.Collection('Players');
var Players = new Mongo.Collection('Players');
Template.adminDashboard.events({
Template['adminDashboard'].events({
'click .givePoints': function () {
Players.update(Session.get("currentPlayer"), {$inc: {score: 5}});
}
@@ -223,14 +226,14 @@ Meteor.methods({
/**
* From Collections, collection.remove section
*/
Template.chat.events({
Template['chat'].events({
'click .remove': function () {
Messages.remove(this._id);
}
});
// DA: I added this next line
var Logs = new Meteor.Collection('logs');
var Logs = new Mongo.Collection('logs');
Meteor.startup(function () {
if (Meteor.isServer) {
@@ -242,7 +245,7 @@ Meteor.startup(function () {
/***
* From Collections, collection.allow section
*/
Posts = new Meteor.Collection("posts");
Posts = new Mongo.Collection("posts");
Posts.allow({
insert: function (userId, doc) {
@@ -282,21 +285,11 @@ topPosts.forEach(function (post) {
count += 1;
});
/**
* From Collections, cursor.count section
*/
var frag = Meteor.render(function () {
var highScoring = Posts.find({score: {$gt: 10}});
return "<p>There are " + highScoring.count() + " posts with " +
"scores greater than 10</p>";
});
document.body.appendChild(frag);
/**
* From Collections, cursor.observeChanges section
*/
// DA: I added this line to make it work
var Users = new Meteor.Collection('users');
var Users = new Mongo.Collection('users');
var count1 = 0;
var query = Users.find({admin: true, onlineNow: true});
@@ -317,23 +310,17 @@ setTimeout(function () {handle.stop();}, 5000);
/**
* From Sessions, Session.set section
*/
Deps.autorun(function () {
Tracker.autorun(function () {
Meteor.subscribe("chat-history", {room: Session.get("currentRoomId")});
});
// Causes the function passed to Deps.autorun to be re-run, so
// Causes the function passed to Tracker.autorun to be re-run, so
// that the chat-history subscription is moved to the room "home".
Session.set("currentRoomId", "home");
/**
* From Sessions, Session.get section
*/
Session.set("enemy", "Eastasia");
var frag1 = Meteor.render(function () {
return "<p>We've always been at war with " +
Session.get("enemy") + "</p>";
});
// Page will say "We've always been at war with Eastasia"
// DA: commented out since transpiler didn't like append()
@@ -362,15 +349,6 @@ Meteor.users.deny({update: function () { return true; }});
/**
* From Accounts, Meteor.loginWithExternalService section
*/
Accounts.loginServiceConfiguration.remove({
service: "weibo"
});
Accounts.loginServiceConfiguration.insert({
service: "weibo",
clientId: "1292962797",
secret: "75a730b58f5691de5522789070c319bc"
});
Meteor.loginWithGithub({
requestPermissions: ['user', 'public_repo']
}, function (err) {
@@ -434,56 +412,16 @@ Accounts.emailTemplates.enrollAccount.text = function (user, url) {
/**
* From Templates, Template.myTemplate.helpers section
*/
Template.adminDashboard.helpers({
Template['adminDashboard'].helpers({
foo: function () {
return Session.get("foo");
}
});
/**
* From Templates, Template.myTemplate.preserve
*/
Template.adminDashboard.preserve({
'input[id]': function (node) { return node.id; }
});
/**
* From Templates, Meteor.render section
*/
var frag2 = Meteor.render(function () {
return "<p>There are " + Players.find({online: true}).count() +
" players online.</p>";
});
document.body.appendChild(frag2);
Players.update({idleTime: {$gt: 30}}, {$set: {online: false}});
/**
* From Templates, Meteor.renderList section
*/
var frag3 = Meteor.renderList(
Posts.find({tags: "frontpage"}),
function(post) {
var style = Session.equals("selectedId", post._id) ? "selected" : "";
// A real app would need to quote/sanitize post.name
return '<div class="' + style + '">' + post.name + '</div>';
});
document.body.appendChild(frag3);
var somePost = Posts.findOne({tags: "frontpage"});
Session.set("selectedId", somePost._id);
var eventTester = {
'click p': function (event: IMeteorEvent) {
var paragraph = event.currentTarget; // always a P
var clickedElement = event.target; // could be the P or a child element
}
}
/**
* From Match section
*/
var Chats = new Meteor.Collection('chats');
var Chats = new Mongo.Collection('chats');
Meteor.publish("chats-in-room", function (roomId) {
// Make sure roomId is a string, not an arbitrary mongo selector object.
@@ -515,16 +453,16 @@ check({ name: undefined }, pat) // Throws an exception
check(undefined, Match.Optional('test')); // OK
/**
* From Deps, Deps.autorun section
* From Deps, Tracker.autorun section
*/
Deps.autorun(function () {
Tracker.autorun(function () {
var oldest = Monkeys.findOne('age = 20');
if (oldest)
Session.set("oldest", oldest.name);
});
Deps.autorun(function (c) {
Tracker.autorun(function (c) {
if (! Session.equals("shouldAlert", true))
return;
@@ -535,29 +473,29 @@ Deps.autorun(function (c) {
/**
* From Deps, Deps.Computation
*/
if (Deps.active) {
Deps.onInvalidate(function () {
Monkeys.destroy();
Rooms.finalize();
});
if (Tracker.active) {
Tracker.onInvalidate(function () {
x.destroy();
y.finalize();
});
}
/**
* From Deps, Deps.Dependency
* From Tracker, Tracker.Dependency
*/
var weather = "sunny";
var weatherDep = new Deps.Dependency;
var weatherDep = new Tracker.Dependency;
var getWeather = function () {
weatherDep.depend()
return weather;
weatherDep.depend();
return weather;
};
var setWeather = function (w) {
weather = w;
// (could add logic here to only call changed()
// if the new value is different from the old)
weatherDep.changed();
weather = w;
// (could add logic here to only call changed()
// if the new value is different from the old)
weatherDep.changed();
};
/**
@@ -608,3 +546,18 @@ Meteor.call('sendEmail',
'Hello from Meteor!',
'This is a test of Email.send.');
var testTemplate = new Blaze.Template();
var testView = new Blaze.View();
declare var el: HTMLElement;
Blaze.render(testTemplate, el);
Blaze.renderWithData(testTemplate, {testData: 123}, el);
Blaze.remove(testView);
Blaze.getData(el);
Blaze.getData(testView);
Blaze.toHTML(testTemplate);
Blaze.toHTML(testView);
Blaze.toHTMLWithData(testTemplate, {test: 1});
Blaze.toHTMLWithData(testTemplate, function() {});
Blaze.toHTMLWithData(testView, {test: 1});
Blaze.toHTMLWithData(testView, function() {});
+603 -577
View File
File diff suppressed because it is too large Load Diff