Why TypeScript lets me add a string and a number? Can I prevent it?

I'm afraid that there is currently no way of preventing such a conversion, aside from building an abstraction of your own. Something like this (admittedly, not very elegant):

function safe_add(x: string, y: string): string { return x + y; }

let x = "1";
x = safe_add(z, 1); // Argument of type '1' is not assignable to parameter of type 'string'.

The usual policy of TypeScript's type checking, according to "All legal JavaScript is legal TypeScript", is to prevent situations which are clearly wrong and never actually useful. For example, passing a string to Math.max is prevented. Unlike this example however, the + operator between a string and a number, despite not always desirable, is indeed a valid operation, and is employed too often in practice to be barred by the compiler. As x += y is equivalent to x = x + y, and always resulting in a string when x is a string, the assignment itself is also valid. This is one of those cases that are most likely going to remain as OK by the compiler. Issue #20131 aims to make a few more operations throw a warning, but this one in particular is not included.

As you might already understand, the opposite is successfully prevented, since a variable is not expected to change its type with the add-assignment operator.

let y = 1;
y += "1"; // Type 'string' is not assignable to type 'number'

See also:

  • What does "all legal JavaScript is legal TypeScript" mean?
  • TypeScript error on implicit type conversion

You can prevent this sort of accident with the TypeScript-ESLint rule restrict-plus-operands:

When adding two variables, operands must both be of type number or of type string.

Config examples:

"restrict-plus-operands": true

If you enable that rule, the following code:

const x = 5;
const y = 'foo';
const z = x + y;
console.log(z);

will result in:

ERROR: .ts - Operands of '+' operation must either be both strings or both numbers, but found 5 + "foo". Consider using template literals.

The rule not only prevents use of + between strings and numbers, but it also prevents use of += when the left-hand side type is different from the right-hand side type.


"It's valid because it's valid in JS" is a non-answer in the context of why a certain operation isn't a type error; see What does "all legal JavaScript is legal TypeScript" mean?

In JavaScript, code like alert("Your position in the queue is " + queuePos) is idiomatic and common -- it is not commonly written as "str" + num.toString().

TypeScript's position is that idiomatic JS should not cause type errors (when practical). This means that string + number is an allowed coercion.

The question of what += should do is then a matter of choosing between two options:

  • consistency: x = x + y should be identical to x += y
  • safety: x += y is not commonly done between string and number operands, so should be an illegal coercion

Both choices are sensible and defensible; TypeScript happened to choose the first.


TSLint suggests to use template literals instead of coercion of string and number variables, eg: 'Hello ' + person.name would become `Hello ${person.name}`

Tags:

Typescript