`unknown` type is coerced to `string`, despite official semantics for `unknown`

Viewed 89

I've read the official doc about the unknown type but I have some difficulties to really understand how it works.

One can read in the doc : "no operations are permitted on an unknown without first asserting or narrowing to a more specific type."

but if I have this function:

const foo = (x: unknown) => {
  return x + "3";
}

it's ok for TypeScript, but this function raises an error, telling that x is unknown:

const foo = (x: unknown) => {
  return x + 3;
}

why does it work with x + "3" ?

If casting is involved, true + 3 or {} + 3 works for instance, as well as true + "3".

2 Answers

This is because Typescript's designers have chosen to allow unchecked many of Javascript's implicit coercions, typically because they are so idiomatic.

Suggestion: stricter operators #7989:

Currently operators like "+" are defined such that they match their semantics in JS. The below are all allowed by the compiler and produce the shown values, even with --strictNullChecks on.

  • 2 + 'a' => "2a"
  • null + 'a' => "nulla" (!)
  • 2 - null => 2

The Typescript maintainers have kept this issue open, suggesting that they are open to changing this, or at least adding a new compiler strictness flag.

Project lead Ryan Cavanaugh says:

Approved behavior change: under --strictNullChecks it should be an error to use a possibly-null/possibly-undefined operand in a +, -, /, *, |, &, ^, or ** expression. One exception is that string + nullable is still OK since that's very common for producing debugging strings.

In type-checking a + binary expression, if at least one operand is a string (in this case "3"), then the compiler infers the whole expression as a string.

Taken from the comments in checkBinaryLikeExpressionWorker (https://github.com/microsoft/TypeScript/blob/main/src/compiler/checker.ts#L32087)

If both operands are of the Number primitive type, the result is of the Number primitive type.
If one or both operands are of the String primitive type, the result is of the String primitive type.

In the case of x + 3, the "number" test fails and in the end, the expression is typed any and an error is reported .

Related