You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/*REQUIREMENTS- state: xy, orientation (N,E,S,W)- behavior: forward,backward,right,left by steps*/typeRobot={x: numbery: numberorientation: "N"|"E"|"S"|"W"forward(): voidbackward(): voidright(): voidleft(): void}constrobot: Robot={x: 0,y: 0,orientation: "E",forward(){switch(this.orientation){case"E":
this.x=this.x+10breakcase"S":
this.y=this.y+10breakcase"W":
this.x=this.x-10breakcase"N":
this.y=this.y-10break}},backward(){switch(this.orientation){case"E":
this.x=this.x-10breakcase"S":
this.y=this.y-10breakcase"W":
this.x=this.x+10breakcase"N":
this.y=this.y+10break}},left(){switch(this.orientation){case"E":
this.orientation="N"breakcase"S":
this.orientation="E"breakcase"W":
this.orientation="S"breakcase"N":
this.orientation="W"break}},right(){switch(this.orientation){case"E":
this.orientation="S"breakcase"S":
this.orientation="W"breakcase"W":
this.orientation="N"breakcase"N":
this.orientation="E"break}}}console.info("TEST robot")console.info("CASE robots moves forward one step"){robot.x=0robot.y=0robot.orientation="E"robot.forward()console.assert(robot.x===10,"robot x is 10")console.assert(robot.y===0,"robot y is 0")console.assert(robot.orientation==="E","robot orientation is E")}console.info("CASE robots moves backward one step"){robot.x=0robot.y=0robot.orientation="E"robot.backward()console.assert(robot.x===-10,"robot x is -10")console.assert(robot.y===0,"robot y is 0")console.assert(robot.orientation==="E","robot orientation is E")}console.info("CASE robots turns left one step"){robot.x=0robot.y=0robot.orientation="E"robot.left()console.assert(robot.x===0,"robot x is 0")console.assert(robot.y===0,"robot y is 0")console.assert(robot.orientation==="N","robot orientation is N")}console.info("CASE robots turns right one step"){robot.x=0robot.y=0robot.orientation="E"robot.right()console.assert(robot.x===0,"robot x is 0")console.assert(robot.y===0,"robot y is 0")console.assert(robot.orientation==="S","robot orientation is S")}console.info("CASE robots turns right one step from N to E"){robot.x=0robot.y=0robot.orientation="N"robot.right()console.assert(robot.x===0,"robot x is 0")console.assert(robot.y===0,"robot y is 0")console.assert(robot.orientation==="E","robot orientation is E")}console.info("CASE robot moves to x 100 and y 50 and ends with orientation N"){robot.x=0robot.y=0robot.orientation="E"for(leti=0;i<10;i++)robot.forward()robot.left()for(leti=0;i<5;i++)robot.backward()console.assert(robot.x===100,"robot x is 100")console.assert(robot.y===50,"robot y is 50")console.assert(robot.orientation==="N","robot orientation is N")}
🙁 Actual behavior
TypeScript throws TS2367: This comparison appears to be unintentional because the types '"E"' and '"N"' have no overlap. after the robot.left() call. This suggests TypeScript incorrectly narrows robot.orientation to the literal type "E" after the assignment robot.orientation = "E", ignoring the mutation in left().
Same applies for the robot.right() method.
🙂 Expected behavior
TypeScript should recognize that robot.orientation can be modified by the left() method to any value in the union type "E" | "S" | "W" | "N". The comparison robot.orientation === "N" should not trigger a TS2367 error, as the left() method can set robot.orientation to "N" when starting from "E".
Same applies for the right() method.
Additional information about the issue
This issue resembles #55215 (), where TypeScript fails to account for class field mutations in methods, and #51586 (), where mutations in async contexts (e.g., setTimeout) are not tracked. In my case, the mutation occurs in a method of a standalone object, not a class or async context, but the core issue seems similar: TypeScript does not track property mutations in method calls.
The error prevents valid test cases from compiling, even though they work correctly at runtime.
A workaround is to use a type assertion (e.g., (robot.orientation as "E" | "S" | "W" | "N") === "N") or a helper function, but this is cumbersome and shouldn’t be necessary for correct code.
The text was updated successfully, but these errors were encountered:
manuelbarzi
changed the title
not tracking potential state change in object literal with union type property
not tracking potential state change in object literal with literal type property
May 25, 2025
manuelbarzi
added a commit
to manuelbarzi/b00tc4mp-ts-202505
that referenced
this issue
May 26, 2025
🔎 Search Terms
TS2367, no overlap, object property mutation, method call, type narrowing, union type
🕗 Version & Regression Information
⏯ Playground Link
https://www.typescriptlang.org/play/?ts=5.9.0-dev.20250525#code/PQKgUASgogigqgSWgWSgOQCoGUwFoAEAzgC4CGxApgFz4AeAngDT4D2ATgJYUB2ZxHLbvgAUaRlEZZGAdQCUefACMKAC1IA3AWxoAzdgHdSbACaNFpAMYBrQycacA5iuKMANhR3El9IpQAOhGAgwGBgxPR+FPgQLIosXgC8+ADeYPjpdDTcAK4AtspsaRn0WXkFRensXLzkAtw0AERoDfgAPvgNUC3tDVjdHdINoRn4emy2xsKyNOosHMYVSpY2RpPT+LPzi47OUzNzCyPunnsbB2AAvqEWgiT4bLHxNDFxiSmLtDQADIyLJfg-RZVHh8OqNLq-RZjCZTd4jEaEfQcYgWFQiYgqDiEAB0wJq-EEsjh8JJFlIhCinQaVEWJJJGKx2No+CSDJxzIA1PgAIxfWl0kaKNgUUhWfmk8mUvo0gUCtnYnyszE4nxc3ni2VCkVi2UZMkUgbUjVy5VMln4eXMgjq3UkrWi416yUdZoy20jeWKi2mnzWvnuwXCh0Cq4jC6QwXLGFE1ICxHI1Ho0140GE4m6-WUrpugPexnMpX5-B+x12oM622ZjrS0vwz3m+sl3Ppe0VjPOhqDHMBy0N02cnn+5ut2tVppG5t5lV9xmqwe1wPajWhjLh4YZY7EWGxunxlFo4TylO1NM79sGqnd91Hzggk9CJLjhcZEfusc1yc36qph8dLrPlty1HDsuwAqdcVvfE6nNXohknV9Kw7V0wK-O8CV-Ts4OHICQ0WNdtg4JwtxjDU90TQ9k0gn8SLfDtsxQyjv3vGC+jAhDzylCdm1QqDBBgwY2JwxCL1Az9GLQ6DH2aQSl1oi9kLExlj3QmD-3goSSRXdIriuMAbm4QgWHcbEOG4PRhAaDAoCwDB7keYgGnkPTbiMigTLMlgLIAYQAQSwKA7NeQh8FyFh1AoYLoVWVhuCiEgKD8RzQjPQL4jNJIh3SB5XgVc1MtS4gIKYlTH3-bZ7OxKKTCmdd0n0wzjPJCk2C3bK0oLBIkl5ZgGjarxmSxQckpGerXOxJqKBa4Q+tyzqMp6vr8B8QavmGvUXMawhmtairlMkzq-waBb7NYKjmMGrp5F00bjNM8yGl8-yCuC0LwuC8xrAmGK4v8JKwBSmaCwBcqcq9fKZr2vjSqGEG0o+lZqqckaNrciapsBlkDtwbqOkWgbgux1b5GRgyxrRnbQcx+bcZO5bgqJxYbtRrbJoptLIYfA6qWO15TuK6CLqS66Ufc+7HoCvrguIbI2AM-BN2+3wEr+gGKqB8GKrB2HCo51SYZGGbNxqxmRfJ6a1apgEefiOh8BWta6tNln0c1y2fhp3m6YBB38CZ8bnbZnWzpKrnmmtrxdcG5oruuEW7s8h6-Il+ypZluWdgj2KlcSpzVZy9Xtdm4GDd24PJMO2qCuxDPjZJhrme28387d8Pbft4n1tJzbG5mxUDvd3rabt+mfb9s2IbLqGub6VvI+CvoY+cru3Pj7yk+ei00+CjPFfivxRgeXJ8DQC0WHwS7ksLguS8pjLC91qT9YyGaa6Rzv6-9nuLbmq2PZt-HvYd0dsvT+rMm5pT7tTQenth6AJNiA8epd+ZT2hrPSeQhBaLz9qvROT1FqvQiqfW2vIvj4FINwYwS18AAFZSHkMoTwYwwUkQYj5hJPi0dL433anlQuWtuFB2QRhMqIwxgiHcF4DgeUADcdt8AAB5ByyI4ByDkQD4QzSqmsSuhsPDEUrmI4QEi5EZWUQomhyjVHqIEdieG0ZK5jwDuAwqHUupfAHnjWBJDR5Oy-rfA6tDW5e1oT4hBTiJ5CMtuONBkSo5CyAA
💻 Code
🙁 Actual behavior
TypeScript throws TS2367: This comparison appears to be unintentional because the types '"E"' and '"N"' have no overlap. after the robot.left() call. This suggests TypeScript incorrectly narrows robot.orientation to the literal type "E" after the assignment robot.orientation = "E", ignoring the mutation in left().
Same applies for the robot.right() method.
🙂 Expected behavior
TypeScript should recognize that robot.orientation can be modified by the left() method to any value in the union type "E" | "S" | "W" | "N". The comparison robot.orientation === "N" should not trigger a TS2367 error, as the left() method can set robot.orientation to "N" when starting from "E".
Same applies for the right() method.
Additional information about the issue
This issue resembles #55215 (), where TypeScript fails to account for class field mutations in methods, and #51586 (), where mutations in async contexts (e.g., setTimeout) are not tracked. In my case, the mutation occurs in a method of a standalone object, not a class or async context, but the core issue seems similar: TypeScript does not track property mutations in method calls.
The error prevents valid test cases from compiling, even though they work correctly at runtime.
A workaround is to use a type assertion (e.g., (robot.orientation as "E" | "S" | "W" | "N") === "N") or a helper function, but this is cumbersome and shouldn’t be necessary for correct code.
The text was updated successfully, but these errors were encountered: