Jess
Jess

Reputation: 650

TypeScript Promise definition generic syntax

I do't quite understand Promise definition of TypeScript as below:

/**
 * Represents the completion of an asynchronous operation
 */
interface Promise<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    /**
     * Attaches a callback for only the rejection of the Promise.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of the callback.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

I think then<TResult1 = T, TResult2 = never> means then has two generic types, which are TResult1 and TResult2. And TResult1 will be T if not specified.

But it turns out that TResult1 will change according to the return type of onfulfilled. See demo :

interface Result {
    status: number;
    message: string;
}

function foo() {
    return new Promise<Result>(function (resolve, reject) {
        resolve({
            status: 0,
            message: 'ok',
        });
     });
}

// here fulfilled1's type is: (local function) fulfilled(out: Result): number
foo().then(function fulfilled1(out) { 
    if (Math.random() > 0.5) {
        return 1;
    }
});
// here fullfilled2's type is: (local function) fulfilled2(out: Result): string
foo().then(function fulfilled2(out) { 
    if (Math.random() > 0.5) {
        return 'hello';
    }
});

both fulfilled1 and fulfilled2 matches then<TResult1 = T, TResult2 = never>. But since I do not sepecify then generic types, I thought TResult1 would be Result in the above code, while in face TResul1 becomes number and string in these case.

Maybe I take something wrong with TypeScript generic . Any ideas are appreciated.

Upvotes: 1

Views: 2292

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249506

The default will be used when no inference can be made for TResult1. In this case an inference can be made based on the function you pass in. Since both parameters to then are marked as optional, this would be a valid call, and will trigger the default:

 foo().then() // returns a promise with TResult1

Or a more useful example, passing in just the second argument, again TResult1 uses the default.

 var r = foo().then(undefined, r => "ERR"); // Promise<string | Result>

If you do pass in a function, that function can change the type of the returned promise, and I think this is the way most people use promises, perform an async call, process the result, return something else to as the next result.

Upvotes: 1

Related Questions