Home > Blockchain >  Why can't the typescript compiler work out that my variables aren't undefined
Why can't the typescript compiler work out that my variables aren't undefined

Time:01-19

I have the following code:

function test(a: number | undefined, b: number | undefined) {
  if (!a && !b) {
    console.log('Neither are present');
    return;
  }

  if (!b && !!a) {
    console.log('b is not present, we only found a - do a thing with a');
    return;
  }

  if (!a && !!b) {
    console.log('a is not present, we only found b - do a thing with b');
    return;
  }

  // At this point, I'd like the compiler to know that both a and b are not undefined,
  // but it doesn't.
  console.log(a   b);
}

The compiler errors out on the final line with the messages 'a' is possibly 'undefined' and 'b' is possibly 'undefined'.

However, it's impossible for the code to get to that point without both a and b existing (i.e. not being undefined).

My if statements are more complicated that you'd expect (i.e. I have !a && !!b instead of just !a) because I want to use the existing parameter if the other parameter is not present.

What have I missed, and is there a more typescripty way to write this logic?

Thanks.

CodePudding user response:

The issue is that none of the if statements actually narrow the types when viewed in isolation. It requires thinking about multiple if statements at the same time to deduce that the types have been narrowed; simple for you and me, not so much for typescript.

Ie, after the first if statement, a and b both are still number | undefined; nothing has changed about their types. The two variables have a correlation, but that's not apparent in their types. So when typescript is evaluating if (!b && !!a) {, all it knows is that both variables are number | undefined. And if that's all you know, then both a and b could still be undefined after the second if.

My if statements are more complicated that you'd expect (i.e. I have !a && !!b instead of just !a) because I want to use the existing parameter if the other parameter is not present.

If you didn't need to use the existing parameter, i would have recommended just deleting the !!b or !!a. But since you do, i recommend rearranging your code to one of the following:

function test(a: number | undefined, b: number | undefined) {
  if (!a || !b) {
    if (a) {
      console.log('b is not present, we only found a - do a thing with a');
      return;
    }
    if (b) {
      console.log('a is not present, we only found b - do a thing with b');
      return;
    }
    console.log('Neither are present');
    return;
  }

  console.log(a   b);
}

Or:

function test(a: number | undefined, b: number | undefined) {
  if (!a) {
    if (!b) {
      console.log('Neither are present');
      return;  
    }

    console.log('a is not present, we only found b - do a thing with b');
    return;
  }
  if (!b) {
    console.log('b is not present, we only found a - do a thing with a');
    return;
  }
  
  console.log(a   b);
}
  • Related