WillyC
WillyC

Reputation: 4705

Proper error pattern for async functions using Promises in TypeScript

I want to make a typed async function with proper error handling.

I can define one like this:

export async function doSomething(userId:string) : Promise<ISomething | void> {

    let somthing: ISomething = {};

    try {
        something.user = await UserDocument.findById(userId);
        something.pet = await PetDocument.findOne({ownerId:userId});
        return Promise.resolve(something);
    } catch (err){
        console.log("I would do some stuff here but I also want to have the caller get the error.");
        return Promise.reject(err);
    }
}

...which seems to work, but (for reasons that are clear), if I try to assign the result to an ISomething object, I get the error Type 'void | ISomething' is not assignable to type 'ISomething'.

let iSomething:ISomething;
iSomething = await doSomething('12'); //this give me the error

I get why that is. My question is, what pattern should I use for error handling in a case like this? Note that if the return type is Promise<IProfile> instead then I get an error for the return Promise.reject(err); line (which would return Profile<void>).

In place of the line return Promise.reject(err); I can use throw err;, but there may be times where I'd want to use the Promise.reject pattern (like if I want to do some more things before I return).

I have a feeling that I'm missing something with promises / async, but I can't find typed examples that follow this pattern.

...note that if I use the full Promise pattern it works fine:

doSomething('12')
  .then( (something) => {//do stuff})
  .catch( (err) => {//handle error});

Should I just be using throw and forget about Promise.reject? If I use throw, will the .catch() be triggered appropriately?

Upvotes: 6

Views: 2214

Answers (2)

WillyC
WillyC

Reputation: 4705

All fair if you don't want to use Promise.reject and Promise.resolve, but if you do - here's the easy fix.

Get rid of the | void in the signature, and change the rejection returned to return Promise.reject<ISomething>(err);. Works!

Here's the modified version from the question:

export async function doSomething(userId:string) : Promise<ISomething> {

    let something: ISomething = {};

    try {
        something.user = await UserDocument.findById(userId);
        something.pet = await PetDocument.findOne({ownerId:userId});
        return Promise.resolve(something);
    } catch (err){
        console.log("I would do some stuff here but I also want to have the caller get the error.");
        return Promise.reject<ISomething>(err);
    }
}

Upvotes: 0

Arjan
Arjan

Reputation: 1113

Not returning a promise in the first place is how I usually implement the async await pattern:

export async function doSomething(userId:string) : Promise<ISomething>{

    let something: ISomething = {};

    try {
        something.user = await UserDocument.findById(userId);
        something.pet = await PetDocument.findOne({ownerId:userId});
        return something;
    } catch (err){
        console.log("I would do some stuff here but I also want to have the caller get the error.");
        throw(err);
    }
}

so if you don't have to do intermediate cleanup you could strip it down to:

export async function doSomething(userId:string) : Promise<ISomething>{    
    let something: ISomething = {};
    something.user = await UserDocument.findById(userId);
    something.pet = await PetDocument.findOne({ownerId:userId});
    return something;
}

and have

  • the subsequent awaited function catch the exception or

  • the calling function handle the rejected promise

Upvotes: 1

Related Questions