3

JavaScript is full of caveats like this one:

const arr = [1, 2, 3]

for (const i in arr) {
  console.log(i + 1)
}

The expected by unexperienced JS developer result would be 1 2 3, but actually, it's 01 11 21.


It looks like TypeScript is not warning about concatenating strings and numbers by default, is there a way to achieve that?

To be more precise: how can I get warnings about cases like '1' + 1

Limon Monte
  • 52,539
  • 45
  • 182
  • 213
  • 1
    You mean besides wrapping the addition in a function? Anyway you'd think that at the very least the compiler would warn you about using `for...in` on an array... – Jared Smith Aug 05 '19 at 20:35
  • I mean always, independently of a place, e.g. in this the simplest case: `console.log('1' + 1)` – Limon Monte Aug 05 '19 at 20:40
  • 1
    `"" + 1` is allowed in JS so it's allows in TS. `console.log()` accepts both strings and numbers so there's no error here. However if you tried to treat `i + 1` as a number you'd get an error. – Aaron Beall Aug 05 '19 at 20:41
  • Sorry about using `console.log` above, it's completely out of scope of my question. I'd really like to get warnings about cases like this: `'1' + 1`. – Limon Monte Aug 05 '19 at 20:46
  • 4
    No way, this has been [proposed and declined](https://github.com/microsoft/TypeScript/issues/16895). `eslint` or `tslint` is your only option. – artem Aug 05 '19 at 20:47
  • `tslint` has been [deprecated](https://medium.com/palantir/tslint-in-2019-1a144c2317a9), do you maybe know what `eslint` rule exactly should I use? – Limon Monte Aug 05 '19 at 20:49
  • @LimonMonte IDK if it's been ported to ESLint yet or not but here's [what you want](https://palantir.github.io/tslint/rules/restrict-plus-operands/) – Jared Smith Aug 05 '19 at 21:29
  • 1
    @AaronBeall It's more nuanced than that... statements like "X is allowed in JS so it's allowed in TS" are usually either [misleading or untrue](https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean). The question is whether the usage in JS is more likely to be a mistake than it is to be valid code; in this case, so much real-world JS code uses constructs like `console.log("There are " + files.length + " files")` that the maintainers of TS do not think it's worth being stricter here. – jcalz Aug 06 '19 at 00:57
  • @jcalz Can't argue with that, it's more nuanced than I stated it. Thanks for the better explanation. :) – Aaron Beall Aug 06 '19 at 14:35

3 Answers3

3

TS Lint can guard against using for..in improperly. https://palantir.github.io/tslint/rules/forin/

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • Thank you, but I'm asking not just about `for..in`, but about general cases, e.g. `'1' + 1` – Limon Monte Aug 05 '19 at 20:47
  • 1
    Also, `tslint` has been [deprecated](https://medium.com/palantir/tslint-in-2019-1a144c2317a9), I'd prefer not to use deprecated linters. – Limon Monte Aug 05 '19 at 20:55
2

UPDATED VALID ANSWER Use the @typescript-eslint/restrict-plus-operands rule. Both operands must either be strings or numbers, but not a mix of both:

The eslint configuration from https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage

{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": "./tsconfig.json"
  },
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/restrict-plus-operands": "error"
  }
}

ORIGINAL

TS Lint has the 'prefer-template' rule, though I haven't tried it with this scenario. It should work, as TypeScript sees the i variable as a string type.

https://palantir.github.io/tslint/rules/prefer-template/

This rule would require you to do the following to make concatenation work:

console.log(`${i}1`);

This should at least signal to the developer that they are trying to perform concatenation.

FYI, TS Lint is being deprecated in favor of ES Lint, though I have not moved any of my projects over to that tool, yet.

UPDATED ES Lint has the same rule: https://eslint.org/docs/rules/prefer-template

Limon Monte
  • 52,539
  • 45
  • 182
  • 213
ps2goat
  • 8,067
  • 1
  • 35
  • 68
  • Thank you @ps2goat. While the `prefer-template` helps with cases like `console.log('1' + 1)`, it's not "smart" enough to handle a bit more complex cases like `const a = '1'; console.log(a + 1)` – Limon Monte Aug 05 '19 at 20:59
  • You're right. I'm now disappointed, as well :( – ps2goat Aug 05 '19 at 21:30
  • @LimonMonte - updated with a new rule that I tested on my machine. – ps2goat Aug 05 '19 at 21:34
  • 1
    Thank you @ps2goat, this is exactly what I was looking for! I updated your answer to have copy-paste friednly eslint config. Thanks man, now my project is more safe from unexpected results! – Limon Monte Aug 06 '19 at 08:42
0

You're concatenating on the arrays property name, not it's value. This is because for...in iterates the properties of an object. I think what you're trying to do is this:

const arr = [1, 2, 3]

for (const i in arr) {
  console.log(arr[i] + 1)
}

You can always write some code in there to validate the types of the inputs, but it is important to first understand the for...in loop - here is some documentation regarding for...in use with arrays

You could use typeof to do a simple type check in your function:

function doConcat(operand1, operand2) {
  if (typeof operand1 !== typeof operand2) { //simple typeof check
    console.log(`Cannot concat type: ${typeof operand1} with type: ${typeof operand2}`);
    return;
  }
  return operand1 + operand2;
}

const arr1 = [1, 2, 3]; //Should work - adding 2 numbers
for (const i in arr1) {
  console.log(doConcat(arr1[i], 1));
}

const arr2 = ['a', 'b', 'c']; //Should bomb - trying to concat number and string
for (const j in arr2) {
  doConcat(arr2[j], 1);
}
Tom O.
  • 5,730
  • 2
  • 21
  • 35
  • I believe I failed to ask the question properly, it's not about how `for...in` works, it's about concatenating of numbers and strings. Concatenating of numbers and strings could lead to some unexpected results, I'd like to avoid that. – Limon Monte Aug 05 '19 at 21:04
  • 1
    gotcha...i will update my answer to reflect that – Tom O. Aug 05 '19 at 21:05