11

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?

user8808265
  • 1,893
  • 1
  • 17
  • 25
  • 1
    The `+` operator between string and number is valid in TypeScript and JavaScript. Why do you want to prevent this? – Explosion Pills Aug 30 '18 at 16:52
  • 1
    Because I would have expected TypeScript to force explicit coercions. The code I've written will 99% of time be a programming error and I want TS to raise them at build time, as long as possible. I can't think of any strongly typed language where this is allowed and makes sense. – user8808265 Aug 30 '18 at 17:07
  • For what is worth, the opposite is illegal: if `k` was a `number`, TypeScript would error on `k += "1"`. – E_net4 Aug 30 '18 at 17:23
  • Relevant TypeScript GitHub Issue: https://github.com/microsoft/TypeScript/issues/30239 – brillout Jan 20 '23 at 22:00

5 Answers5

12

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

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • 1
    Many thanks. This is indeed way more convincing than the "All JS is valid TS" thing. I find `"str" + (5).toString()` more explicit than the idiomatic form (which I find dangerous), but this is very likely to come from my own background and sensibility. – user8808265 Aug 30 '18 at 19:52
  • Oh, and I just read the SO question you linked and it really puts all this and TS choices into perspective. – user8808265 Aug 30 '18 at 20:07
  • So to summarize, by design TS allows `string` + `number` and will not throw an error during compilation? We just need to accept it? That's a little disappointing I would say. – keylogger Aug 09 '22 at 07:28
  • Although `"str" + 3` may be idiomatic (to some people -- I never think it that way), it is not allowed in many other strong typed language -- C, C++, Java, Python etc. If I had wanted all the baggage of JavaScript I would not be using TS. I have already had a few instances of unintended use of string + number that is not reported – Zhe Aug 14 '23 at 10:59
6

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.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
2

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:

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • 2
    +1. I think the fact that the variable type doesn't change is indeed a major argument in favor of not disallowing this. Even though I tend to think it's a bit too permissive, it's not as bad as it seems, and the rest is a matter of taste which has nothing to do on SO :-) . Thank you very much for your time. – user8808265 Aug 30 '18 at 20:02
  • 3
    @user8808265 Indeed, it mostly comes down to how the language has been designed. As an appreciator of static typing, I also prefer my code not to make surprising assumptions. For example, Rust (the language I'm currently more active on here) has very few implicit coercions. – E_net4 Aug 30 '18 at 20:17
1

In short: because it is valid JS and Typescript as @Explosion Pills already pointed out.

The reason is k += 1 is just syntactic sugar for k = k + 1 which one would expect to be 11. So something like this also works:

let text = "hello ";
text += "world!"
console.log(text); 

Which ends up being hello world!

Christian S.
  • 295
  • 1
  • 2
  • 12
  • Well, in your example, you concatenate a string with a string. The question was definitely not about the `+=` operator. – user8808265 Aug 30 '18 at 17:30
  • 1
    @user8808265 well typescript is just a superset of javascript , the syntax that you are using is valid in javascript too. – Niladri Aug 30 '18 at 17:32
  • @Niladri `let i = 1; i = 1 + "";` is valid in JS and not in TS because it changes "i" type. So it's probably not the real reason. – user8808265 Aug 30 '18 at 17:41
1

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

Islam Murtazaev
  • 1,488
  • 2
  • 17
  • 27