microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.02k stars 12.49k forks source link

Type narrowing of condition leads to unexpected error for assignment #60014

Closed krisztianb closed 1 month ago

krisztianb commented 1 month ago

🔎 Search Terms

type narrowing, Type 'number' is not assignable to type '-1', constant array

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/MYewdgzgLgBANgUwG4LgeQE4BMEZgXhgG0BaARgBoZyqbrL7aG6WyBdAbgCgBLAMxgAKRCnTZcAOmS4AnoMFIAhnACuCAJQEAfDCWqEBfIXLrNAby4wr8aWJwYiABjYEYZbgF8gA

💻 Code

const levelOrder = [-1, -1, -1, -1, -1, -1, -1, -1, -1];
if (levelOrder.every((value) => value === -1)) {
    levelOrder[0] = 1;
}

🙁 Actual behavior

The assignment gives the following TS error:

Type 'number' is not assignable to type '-1'.ts(2322) const levelOrder: -1[]

The element in the array cannot be overwritten.

🙂 Expected behavior

The element in the array can be overwritten by any number.

Additional information about the issue

Maybe related to this new feature: https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#control-flow-narrowing-for-constant-indexed-accesses

MartinJohns commented 1 month ago

IMO this is intentional and an unfortunate side-effect of the feature introduced in 5.5 (the one you linked) combined with the existence of the every() type guard overload.

You can simply add a type annotation to prevent the type-guard overload from being chosen:
levelOrder.every((value): boolean => value === -1)

jcalz commented 1 month ago

Isn't it inferred type predicates and not the feature linked above?

krisztianb commented 1 month ago

Thank you for the replies and suggestions to adjust my code. If this works as intended I'm going to close this.