Presumably you think that adding two string | number
operands should succeed and result in a string | number
output. Or more generally that the binary +
operator should distribute over unions in the types of its operands.
My research indicates the current behavior is as designed, but that it is possibly a design limitation or a missing feature. Some of the numerical operators in TypeScript don't behave exactly as people would like, but the broken or missing use cases aren't critical enough to warrant being addressed (at leas not immediately). In this case, I don't see any evidence that people care enough about adding two string | number
operands with +
for it to be explicitly supported.
As far as I can tell there are no GitHub issues, open or closed, asking for such support.
Note that it is generally intentional that some valid JavaScript code is considered invalid by TypeScript. See What does "all legal JavaScript is legal TypeScript" mean? for a canonical explanation. That means we have to say something more than "this works in JavaScript" to justify it being allowed in TypeScript. We have to say "this code is likely to be intentional and not a bug".
It is not obvious that adding two string | number
together is something people want to do very often; the operations of string concatenation and numeric addition are very different operations that happen to be accessible via +
. You can think of +
as an overloaded function, and TypeScript does not allow you to call overloaded functions with unions of the parameter types from each call signature; see microsoft/TypeScript#14107 for a feature request to allow such calls.
But it's possible someone could open an issue requesting support for adding two string | number
with proper justification about it being intentional more often than it is a bug.
I think such an issue wouldn't do much good though. If we look at microsoft/TypeScript#2031, we see a related issue about why you cannot add two values of type Number
(the wrapper object, not the number
primitive). At the time this was filed, the TypeScript language specification said that the binary +
operator is allowed in an expression a + b
if
- the types of both
a
and b
are assignable to number
, in which case the result is a number
; or
- the types of either
a
or b
are assignable to string
, in which case the result is a string
; or
- the types of either
a
or b
are any
, in which case the result is an any
.
None of those allow for Number
(or indeed string | number
). It is likely that those three possibilities account for the vast majority of usages for binary +
, and while it might be nice to support other cases, they are not urgent. Support for things like Number
was proposed in microsoft/TypeScript#2361, where it has languished for years without being implemented.
So it clearly isn't critical to anyone on the TS team.
If you want to work around this yourself, you could always do so, like
const add = (a: string | number, b: string | number) =>
((a as any) + b) as (string | number);
which would alwas produce string | number
, or the following distributive conditional generic thing:
const add = <T extends string | number, U extends string | number>(t: T, u: U) =>
((t as any) + u) as (T extends number ? U extends number ? number : string : string);
const five = add(2, 3); // number
const ab = add("a", "b"); // string
const aThree = add("a", 3); // string
const twoB = add(2, "b"); // string
const alsoTwoB = add(Math.random() < 0.5 ? "2" : 2, "b"); // string
const roulette = add(Math.random() < 0.5 ? "2" : 2, 3); // string | number
which produces a narrow output type that depends on the type of each of the inputs.
Playground link to code