It seems that the idea of adding two string | number
operands to yield a string | number
output is up for debate. The question arises as to whether the binary +
operator should extend its functionality to handle unions in the types of its operands.
From my investigation, the current behavior appears intentional but could be considered either a design limitation or a missing feature. While some numeric operators in TypeScript may not align perfectly with user expectations, the issues raised by such use cases do not seem significant enough to warrant immediate attention. In the case of combining two string | number
operands with +
, there doesn't appear to be strong demand for explicit support.
As far as I can tell, no GitHub issues—open or closed—exist requesting this specific type of support.
It's worth noting that TypeScript deliberately flags certain valid JavaScript code as invalid. Check out What does "all legal JavaScript is legal TypeScript" mean? for further explanation on this point. Merely stating that code works in JavaScript is not sufficient justification for allowing it in TypeScript; there needs to be evidence that the code is intentional and not a bug.
The need to add together two string | number
values might not be a common requirement; string concatenation and numerical addition are distinct operations facilitated by the same +
operator. It can be viewed as an overloaded function. TypeScript restricts calling overloaded functions using unions involving parameter types from multiple signatures; refer to microsoft/TypeScript#14107 for a feature request related to this behavior.
An open issue advocating for supporting the addition of two string | number
values may face limited traction. A similar issue regarding the inability to add two values of type Number
remains unresolved (see microsoft/TypeScript#2031). This suggests that accommodating non-conventional cases like Number
has been proposed but neglected over time.
Ultimately, this matter does not appear critical to the TypeScript team's priorities.
To address this limitation independently, one workaround involves:
const add = (a: string | number, b: string | number) =>
((a as any) + b) as (string | number);
This approach consistently yields a string | number
output. Alternatively, you can explore a distributive conditional generic solution, such as the following:
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
This function produces narrow output types based on the inputs' respective types.
Access the Playground link here