Home > Software design >  problem in typing Promise that returns a Union of types
problem in typing Promise that returns a Union of types

Time:04-04

(using TypeScript 4.6.3)

I have the following SSCCE code that represents the outcome of a login operation. For some reason unrelated to this question, I want to handle the case of bad password under a resolve branch:

Typescript Playground is here.

enum LoginOutcome {SUCCESS, FAIL}

type LoginResults = {
    outcome: LoginOutcome.SUCCESS
    person_name: string
} | {
    outcome: LoginOutcome.FAIL
}


function login(): Promise<LoginResults> {
  const promise: Promise<LoginResults> = new Promise( (resolve, reject) => {

    setTimeout(()=>{
      const r1 = {outcome: LoginOutcome.SUCCESS, person_name: "John"};
      const r2 = {outcome: LoginOutcome.FAIL};

      if (Math.random() <= 0.5)
         return resolve(r1);
      else
        return resolve(r2); // TS complains: Type 'LoginOutcome' is not assignable to type 'LoginOutcome.FAIL'.(2345)
      
    }, 3000);
  });
  return promise;
}

The above code does not typecheck as indicated in the comment. I don't understand why. Also, I have discovered that if I change the line:

const r2 = {outcome: LoginOutcome.FAIL};

to:

const r2: LoginResults = {outcome: LoginOutcome.FAIL};

… then the code typechecks. This makes no sense at all as TS should have been able to infer that {outcome: LoginOutcome.FAIL} is a valid instance of type LoginResults.

CodePudding user response:

The problem is with the way that TypeScript infers data in non-as const expressions. When you have literal values such as enum members or strings or numbers, it will simply resolve to the generic enum type / string / number. Therefore, the following assignment:

const r2 = {outcome: LoginOutcome.FAIL};

Actually resolves to this type:

declare const r2: { outcome: LoginOutcome }; // not specifically `LoginOutcome.FAIL`

When you type r2 to LoginResults, it is forced to see if it matches the literals because the discriminated union uses literals for the values. Therefore, it can correctly type check as you expect it to.

  • Related