Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I declare a Generic Promise in Typescript, so that when the Generic type is "<void>", one of its methods won't take a parameter?

In Typescript, I want to be able to define a Promise's type in such a way so that I can do this:

//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();
}

The promise definition files I've seen all declare that the "resolve" method of a promise must take a value. Here is a recent example from the wonderful DefinitelyTyped project:

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

```

That basically says, "The resolve callback must be passed a value of type R." That's fine for a promise like new Promise<number>. Typescript will verify we're calling resolve with a value of type number.

However, what if I want a promise that doesn't have a value, so I want to be able to call resolve() without passing a value? I can declare my promise like this: new Promise<void> But then I'm still forced to call resolve and pass in a value of some sort. I can call resolve(undefined), but that reads a bit strangely.

There appears to be no way to properly capture this concept in Typescript: "If this generic has a type 'void', then don't expect a parameter for this function."

The closest I can do is mark the result as optional in the resolve method, but that would mean that the result is always optional, even for typed versions of Promises.

like image 848
Taytay Avatar asked Jul 03 '14 13:07

Taytay


People also ask

How do you define a Promise type in TypeScript?

To declare a function with a promise return type, set the return type of the function to a promise right after the function's parameter list, e.g. function getPromise(): Promise<number> {} . If the return type of the function is not set, TypeScript will infer it.

How do I write a Promise function in TypeScript?

const promise = new Promise((resolve, reject) => { // Code to execute }); In example above, a promise takes callback function as a parameter. Its callback function has 2 parameters resolve and reject. If the condition inside promise is true then the promise returns resolve else it returns the reject.

How do you pass a generic type in TypeScript?

Assigning Generic ParametersBy passing in the type with the <number> code, you are explicitly letting TypeScript know that you want the generic type parameter T of the identity function to be of type number . This will enforce the number type as the argument and the return value.

Are there promises in TypeScript?

The promise in TypeScript is used to make asynchronous programming. The promise can be used when we want to handle multiple tasks at the same time. By the use of TypeScript promise, we can skip the current operation and move to the next line of the code.


3 Answers

This does work!
The promise definitions were updated to support such a use case.

Runnable example is too long to be posted. But just copy/paste newest definitions and your Promise code into a an editor and you will see that your example now works.

like image 154
Anders Avatar answered Nov 08 '22 12:11

Anders


From the way you want to use the Promise interface it seems that you want to sometimes pass a value and sometimes you want to pass no value, so thats what optional parameters are for. If resolving a promise with 'undefined' result is not acceptable (IMO this isnt a bad idea, this tells exacly what is going on in the code - the result of promise is udefined) I can propose a solution that seems to be what you are looking for:

I would define a 'contract' for the promise, lets say:

export interface IMyPromiseResult {
    result: number;
}

and the use it with the promise:

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

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

   resolve(<IMyPromiseResult>{ });
}

While this approach is a bit more complicated, it opens some interesting options... On the other hand - current DefinitelyTyped Promise constructor is defined as:

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

so having an optional result is allowed and you should be fine with it.

like image 43
Slawek Avatar answered Nov 08 '22 12:11

Slawek


I might have just come up with a workaround I'm pleased with. In the case where I want to have a Promise<void> that ensures the resolve callback does not take a parameter, rather than making the resolve method always take an optional parameter, I can define a new class like so:

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);
    }
}

And in that case, I can use it like so:

    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"));
                }
            })
        });
    }

Granted, devs will have to use the VoidPromise instead of simply "Promise", but the intended effect is achieved without having to falsely mark the resolve parameter as optional.

For my scenario, the above code meets my expectations more than marking all resolve methods as having an optional result. Marking all results as optional feels dangerous in the 99% case. If it's always optional, I can declare a Promise<number>, call resolve() without a result, and get an undefined result for my promise. In that case, I should have rejected the promise. I don't believe it is expected that the resolve method's parameter is truly optional. (Source: https://github.com/domenic/promises-unwrapping#the-promise-constructor)

like image 2
Taytay Avatar answered Nov 08 '22 13:11

Taytay