【发布时间】:2021-03-19 20:09:54
【问题描述】:
我正在使用流星.js 和 TypeScript 并尝试制作强类型的流星方法。
为此,我创建了一个文件,其中包含我的方法的类型定义,如下所示:
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;
现在我正在尝试创建一个方法,将普通的流星方法(使用回调...)包装到一个返回如下承诺的函数中:
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);
});
});
}
这似乎是一种魅力。我可以等待这样的流星方法
const retVal = await meteorCallAsync<NewGameMethod>("newGame", [getClientWithSecret()]);
TypeScript 实际上会检查字符串"newGame" 是否等于定义为NewGameMethod 名称的字符串文字。完美,但我有两个问题,因为我是 TypeScript 的新手:
-
是否可以完全省略
meteorCallAsync的第一个参数并让 TypeScript 编译器填充它?我已经将类型定义为泛型,所以编译器确实有必要的信息来填写它,但我不知道 TypeScript 是否支持这一点 -
有没有办法将
MeteorMethod接口定义为某种无法实例化的抽象接口?ValidMethods实际上是否是更适合meteorCallAsync<T extends ValidMethods>的类型?有没有办法让我强制每个方法实际上必须有name、args和return?
编辑:
我在下面添加了newGame 方法的实现。问题是我不知道如何告诉 TypeScript Meteor.call(name, ...args, (error, result)=>{}) 实际上调用了 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
},
});
【问题讨论】:
-
不需要您的接口,因为您可以使用ReturnType<T> 和Parameters<T> 从函数类型派生
args和return。您只需要知道从方法名称到方法类型的关联。您可以创建一个接口,其中键是名称,值是方法,但这可能已经存在于 Meteor 类型中。我将查看 Meteor 包类型,以便给您一个可靠的答案。 -
好的,
Meteor.call的定义非常松散且无用function call(name: string, ...args: any[]): any;似乎您使用Meteor.methods()定义了方法,因此我们可以利用它来获取应用程序方法的类型. -
我已经添加了我对 newGame() 流星方法的实现,并解释了为什么我试图用我自己的类型来定义它,希望这很有意义。如果有更高级的方法可以直接将
Meteor.call耦合到没有我的解决方法的方法,我当然会很高兴听到它们。 -
您可以查看声明合并。我认为您正在做的事情很好,但是您可以用更少的 TS 代码来做到这一点。我正在为你解答:)
标签: typescript meteor typescript-typings string-literals