如何在Typescript中声明Generic Promise,以便当Generic类型为&#34; <void>&#34;时,其中一个方法不会获取参数?</void>

时间:2014-07-03 13:54:33

标签: typescript definitelytyped

在Typescript中,我希望能够以这样的方式定义Promise的类型,以便我可以这样做:

//This works today:
new Promise<number>((resolve)=>{
   //Cool
   resolve(5);
   //Error, because I didn't pass a number:
   resolve();
}

//This is what I want to do also:
new Promise<void>((resolve)=>{
   //Error, because I passed a value:
   resolve(5);
   //Cool, because I declared the promise to be of type void, so resolve doesn't take a value:
   resolve();
}

我见过的promise定义文件都声明promise的“resolve”方法必须带一个值。 Here是精彩的DefinitelyTyped项目的最新示例:

declare class Promise<R> implements Thenable<R> {
    constructor(callback: (resolve : (result: R) => void, reject: (error: any) => void) => void); 
///...
}

```

基本上说,“解析回调必须传递一个R类型的值。”这对于像new Promise<number>这样的承诺来说很好。 Typescript将验证我们是否使用number类型的值调用resolve。

但是,如果我想要一个没有值的promise,那么我希望能够在不传递值的情况下调用resolve()?我可以这样声明我的承诺:new Promise<void> 但是后来我仍然被迫调用resolve并传入某种值。我可以打电话给resolve(undefined),但这听起来有点奇怪。

似乎无法在Typescript中正确捕获此概念:“如果此泛型具有'void'类型,则不要指望此函数的参数。”

我能做的最接近的是在resolve方法中将结果标记为可选,但这意味着结果始终是可选的,即使对于Promises的类型版本也是如此。

3 个答案:

答案 0 :(得分:4)

这确实有效!
promise definitionsupdated以支持此类用例。

Runnable示例太长,无法发布。但只需将最新的定义和Promise代码复制/粘贴到编辑器中,您就会看到您的示例现在有效。

答案 1 :(得分:2)

从你想要使用Promise接口的方式来看,你似乎希望有时传递一个值,有时你想不传递任何值,这就是可选参数的用途。 如果使用&#39; undefined&#39;解决承诺?结果是不可接受的(IMO这不是一个坏主意,这可以告诉我代码中发生了什么 - 承诺的结果 udefined)我可以提出一个看似你正在寻找的解决方案为:

我会定义一份合同&#39;对于承诺,让我们说:

export interface IMyPromiseResult {
    result: number;
}

并将其与承诺一起使用:

new Promise<IMyPromiseResult>((resolve)=>{

   resolve(<IMyPromiseResult>{ result: 5 });

   resolve(<IMyPromiseResult>{ });
}

虽然这种方法有点复杂,但它会打开一些有趣的选项...... 另一方面 - 当前的DefinitelyTyped Promise构造函数定义为:

constructor(callback: (resolve: (result?: R) => void, reject: (error: any) => void) => void);

因此允许使用可选结果,你应该没问题。

答案 2 :(得分:2)

可能刚刚提出了一个我很满意的解决方法。如果我想要Promise<void>确保resolve回调获取参数,而不是让resolve方法始终采用可选参数,我可以像这样定义一个新类:

export class VoidPromise extends RSVP.Promise<void>{
    //Note that resolve does not take a parameter here:
    constructor(callback:(resolve:() => void, reject:(error:any) => void) => void){
        super(callback);
    }
}

在这种情况下,我可以像这样使用它:

    public static testVoidPromise() : VoidPromise{
        return new VoidPromise((resolve, reject)=>{

            setTimeout(1000, ()=>{
                if (Math.random() < 0.5){
                    //Note that resolve() does NOT take a parameter
                    resolve();
                }else{
                    reject(new Error("Something went wrong"));
                }
            })
        });
    }

当然,开发人员必须使用VoidPromise而不是简单的“Promise”,但实现了预期的效果,而不必将resolve参数错误地标记为可选。

对于我的场景,上面的代码符合我的期望,而不是将所有 resolve方法标记为具有可选结果。将所有结果标记为可选项在99%的情况下感觉很危险。如果它始终是可选的,我可以声明Promise<number>,在没有结果的情况下调用resolve(),并为我的承诺获得undefined结果。在那种情况下,我应该拒绝承诺。我不相信解析方法的参数是真正可选的。 (来源:https://github.com/domenic/promises-unwrapping#the-promise-constructor