1

Here is the code.

type Combinable = string | number;

function add(n: Combinable, b: Combinable) {
    if (typeof n === 'string'&& typeof b ==='string') {
        return n.toString() + b.toString();
    } else if (typeof n === 'string'&& typeof b ==='number') {
        return n.toString() + b.toString();
    } else if (typeof n === 'number'&& typeof b ==='string') {
        return n.toString() + b.toString();
    } else {
        // here raises error
        // Operator '+' cannot be applied to types 'Combinable' and 'Combinable'.ts(2365)
        return n + b;
    }
}

// But it works fine when I change "and" operator to "or" operator.
function add(n: Combinable, b: Combinable) {
    if (typeof n === 'string' || typeof b ==='string') {
        return n.toString() + b.toString();
    } else {
        return n + b;
    }
}

As you can see, I got the error on the last line n + m.

I thought it should work if I cover all cases, but the error still remains.

What am I missing here?

2 Answers 2

1

The problem is that the type comparisons you're doing in the if statements with && can't narrow down the type of any individual parameter.

After doing

if (typeof n === 'string' && typeof b ==='string') {

TS isn't smart enough to recognize that this means that either n or b is not a string: both n and b are still typed as Combinable.

Using ||, on the other hand, makes it parseable, because it's absolutely certain that both n and b are not strings after that branch ends.

One way to fix it would be to nest the if/elses:

function add(n: Combinable, b: Combinable) {
    if (typeof n === 'string') {
        if (typeof b === 'string') {
            return n.toString() + b.toString();
        } else if (typeof b === 'number') {
            return n.toString() + b.toString();
        }
    } else if (typeof n === 'number') {
        if (typeof b === 'string') {
            return n.toString() + b.toString();
        } else {
            return n + b;
        }
    }
}

(can also use just else instead of else if(cond), since there are no other alternatives)

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you first, I understand it as following, correct me if I am wrong plz. When I use (A && B) condition, TS doesn't know A evaluation result when it evaluates B condition. Hence the error raises. When using | operator, neither n nor b are string, so both are evaluated as the remaining type number. That's why it doesn't complain. Am I right?
Not exactly - Inside the branch, they're narrowed properly, but after exiting the branch and going to an else, TS can't know why the branch wasn't taken - it doesn't take into consideration the two or three possible type combinations that produces that outcome. If you do A && B, you narrow down A and B inside that branch, but not outside it. If you do A || B, you narrow down A and B outside that branch, but not inside it.
0

type Param = string | number;

function add(a: Param, b: Param) {
  if (typeof a === 'number' && typeof b === 'number') {
    return a + b;
  }
  return a.toString() + b.toString();
}

add(1, '2');
add(1, 2);
add('1', '2');

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.