Currently experimenting with meteor.js and TypeScript to create strongly typed meteor methods. I have defined type definitions for my methods in a file as follows:
export interface ClientWithSecret {
id: string;
secret: string;
}
export interface MeteorMethod {
name: string;
args: any[];
return: any;
}
export interface NewGameMethod extends MeteorMethod {
name: "newGame";
args: [auth: ClientWithSecret];
return: string;
}
export interface NewClientMethod extends MeteorMethod {
name: "newClient";
args: [];
return: ClientWithSecret;
}
export interface LoginMethod extends MeteorMethod {
name: "login";
args: [auth: ClientWithSecret];
return: true | ClientWithSecret;
}
export type ValidMethods = NewGameMethod | NewClientMethod | LoginMethod;
Now, I am working on wrapping normal meteor methods with callbacks into functions that return promises like so:
export function meteorCallAsync<T extends MeteorMethod>(methodName: T["name"], args: T["args"]): Promise<T["return"]> {
return new Promise((resolve, reject) => {
Meteor.call(methodName, ...args, (error: Meteor.Error, result: T["return"]) => {
if (error) {
reject(error);
}
resolve(result);
});
});
}
This implementation works perfectly fine. I can await a meteor method call like this:
const retVal = await meteorCallAsync<NewGameMethod>("newGame", [getClientWithSecret()]);
I do have a couple of questions since I'm new to TypeScript:
Is it possible to exclude the first parameter in
meteorCallAsync
and let TypeScript automatically fill it in based on the defined generic type?Can the
MeteorMethod
interface be defined as an abstract interface that cannot be instantiated? Also, would it be more appropriate to useValidMethods
as the type for
and how can I enforce each method to have ameteorCallAsync<T extends ValidMethods>
name
,args
, andreturn
?
EDIT:
Below is my implementation of the newGame
method. The challenge lies in informing TypeScript that
Meteor.call(name, ...args, (error, result)=>{})
actually triggers the function defined in Meteor.methods
.
Meteor.methods({
// create a new game
newGame(auth: ClientWithSecret) {
if (!isValidClient(auth)) {
console.error(`client invalid ${auth.id}`);
return;
}
let randomId,
newIdFound = false;
while (!newIdFound) {
randomId = Random.id();
const game = GamesCollection.findOne({ _id: randomId });
if (!game) {
newIdFound = true;
}
}
GamesCollection.insert({
_id: randomId,
hostId: auth.id,
clientIds: [auth.id],
players: [],
createdAt: new Date(Date.now()),
});
return randomId;
},
newClient(): ClientWithSecret {
//implementation
},
login(auth: ClientWithSecret): true | ClientWithSecret {
// returns true if login successful, new ClientWithSecret if credentials invalid
},
});