Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

typescript

The following code

let k = "1";
k += 1;
console.log(k);

compiles correctly using TypeScript, even in strict mode. I would have expected tsc to fail with some error like Cannot add a string and an integer.. Why does it build successfuly? Can I prevent this dangerous behavior?

like image 956
user8808265 Avatar asked Aug 30 '18 16:08

user8808265


3 Answers

"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.

like image 65
Ryan Cavanaugh Avatar answered Jan 04 '23 21:01

Ryan Cavanaugh


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.

like image 28
CertainPerformance Avatar answered Jan 04 '23 21:01

CertainPerformance


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
like image 39
E_net4 stands with Ukraine Avatar answered Jan 04 '23 19:01

E_net4 stands with Ukraine