简体   繁体   中英

TypeScript Discriminated Union - Variable Used Before Assigned after Exhaustive If Statements

I am writing some code using TypeScript's discriminated union feature. I am exhaustively checking the "type" property for each type in an if-else if structure and assigning a variable using logic in each block. Then, I am returning the variable. However, I am getting an error about the returned variable being used before it is assigned. The code follows this structure (this is a really simple example demonstrating the problem since my actual code is much more complex):

interface One
{
  num   : "one";
  value : string;
}

interface Two
{
  num   : "two";
  value : number;
}

interface SomeComplexObject
{
  // Complex properties
}

type Num  = One | Two;

function isOne(
  obj : Num
) : SomeComplexObject
{
  let complex : SomeComplexObject;

  if (obj.num === "one")
  {
    // Create a complex object with one set of parameters
  }
  else if (obj.num === "two")
  {
    // Create a complex object with a second set of parameters
  }

  return complex;
}

My first thought was to add a final else to throw an error saying that (in this case) the value for num is invalid, but since the other conditions are exhaustive, obj is of type never and cannot be used.

I could throw a super generic error and be done with it, but I am wondering why the compiler thinks complex could be undefined but at the same time recognize that the conditions are exhaustive. Is there something I am missing?

Edit : My original example seems to have caused some confusion. I updated it to hopefully be more representative of the actual problem.

I handle these sorts of scenarios one of two ways. Usually go with the exhaustive switch.

interface One {
  num   : "one";
  value : string;
}

interface Two {
  num   : "two";
  value : number;
}

interface SomeComplexObject {
  // Complex properties
}

type Num = One | Two;

// Exhaustive Switch
function isOne(obj : Num) : SomeComplexObject {
    let complex: SomeComplexObject;

    switch (obj.num) {
        case "one":
            complex = {};
        case "two":
            complex = {};
    }

    return complex;
}

// Cast obj to any in `never` case
function isOne2(obj : Num) : SomeComplexObject {
    let complex: SomeComplexObject;

    if (obj.num === "one") {
        complex = {};
    } else if (obj.num === "two") {
        complex = {};
    } else {
        throw new Error(`Unknown object type ${(obj as any).num}`)
    }

    return complex;
}

Typescript Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM