-
-
Notifications
You must be signed in to change notification settings - Fork 161
fix(runtime): in operator throws TypeError on a non-object right operand
#6024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Node.js "in" operator TypeError non-object right operand does it call toString on left operand💡 Result:
The JavaScript "in" operator does not call
toStringon its left-hand operand to facilitate a search in the right-hand operand; rather, it converts the left-hand operand to a property key using the abstract operationToPropertyKey[1][2][3]. The "TypeError: Cannot use 'in' operator to search for 'x' in 'y'" (or similar errors involvingundefinedornull) occurs because the right-hand operand of theinoperator must be an object [4][5][6]. If the right-hand side is a primitive (such asstring,number,boolean,undefined, ornull), JavaScript throws aTypeError[4][5][6]. Regarding the operands: 1. Left-hand operand: Theinoperator evaluates the left-hand operand usingToPropertyKey, which converts the value to either a string or a symbol [1][2]. This ensures the value can be used as a property key [1][2]. 2. Right-hand operand: This must be an object (or an object-like value that is notnullorundefined) [4][5][6]. If you attempt to use theinoperator on a primitive type, the engine immediately throws theTypeErrormentioned above [4][5][6]. Commonly, developers encounter this error when they inadvertently pass a primitive (like a string,null, orundefined) to the right side of theinoperator, often when expecting an object [5][7][8]. To fix this, you should ensure the right-hand side is a valid object before using theinoperator, or use alternative methods (such asArray.prototype.includes()orString.prototype.includes()) if your intent was to search for a value inside a collection rather than a property in an object [5][6].Citations:
🏁 Script executed:
Repository: PerryTS/perry
Length of output: 50369
🏁 Script executed:
Repository: PerryTS/perry
Length of output: 16877
🌐 Web query:
ECMAScript in operator algorithm ToPropertyKey ToObject evaluation order spec relationalexpression in shiftexpression💡 Result:
In ECMAScript, the evaluation order of the relational expression involving the in operator is strictly left-to-right [1]. According to the Runtime Semantics for RelationalExpression: RelationalExpression in ShiftExpression, the evaluation steps are as follows [1]: 1. Let lRef be the result of evaluating the left-hand side (RelationalExpression) [1]. 2. Let lVal be? GetValue(lRef) [1]. 3. Let rRef be the result of evaluating the right-hand side (ShiftExpression) [1]. 4. Let rVal be? GetValue(rRef) [1]. 5. If rVal is not an Object, throw a TypeError exception [1]. 6. Return? HasProperty(rVal,? ToPropertyKey(lVal)) [1]. The left-hand operand is evaluated and converted to a value (lVal) before the right-hand operand is evaluated (rVal) [1]. The ToPropertyKey abstract operation is then applied to the result of the left-hand operand (lVal) to produce the property key [1][2]. The right-hand operand (rVal) must be an Object; if it is not, a TypeError is thrown [1]. Finally, the HasProperty abstract operation is performed to determine if the property key exists on the object [1].
Citations:
🏁 Script executed:
Repository: PerryTS/perry
Length of output: 18250
🏁 Script executed:
Repository: PerryTS/perry
Length of output: 22501
🌐 Web query:
site:tc39.es/ecma262 in operator algorithm ToPropertyKey ToObject right operand primitive before ToPropertyKey💡 Result:
The ECMAScript specification defines the runtime evaluation of the in operator within the RelationalExpression production. The evaluation algorithm does not perform ToObject on the right operand before ToPropertyKey on the left operand; rather, it typically involves evaluating both sides and then performing property existence checks [1][2]. Specifically, the runtime evaluation for the RelationalExpression
lval in rvalproceeds as follows: 1. The left operand is evaluated, and the result is retrieved using GetValue. 2. The right operand is evaluated, and the result is retrieved using GetValue. 3. The left operand's value is converted to a property key using the ToPropertyKey abstract operation [3][4]. 4. The right operand's value is checked to ensure it is an Object; if it is not, a TypeError is thrown. 5. Finally, the HasProperty abstract operation is performed on the object (right operand) using the property key (left operand) to determine if the property exists [3][4]. Because the in operator requires the right operand to be an object (or specifically, it throws if it is not), the algorithm enforces this check before attempting to perform property lookups, and it does not implicitly invoke ToObject on the right operand [3][4]. ToPropertyKey is applied to the left operand to ensure it is a valid property key (a String or Symbol) [3][4].Citations:
🌐 Web query:
ECMAScript 2025 in operator ToPropertyKey ToObject "Cannot use 'in' operator" left operand object side effect💡 Result:
In ECMAScript 2025 (the 16th edition of the ECMA-262 specification), the relational
inoperator evaluates its operands by first evaluating the left-hand side (the property name) and then the right-hand side (the object) [1][2][3]. The abstract operationsToPropertyKeyandToObjectare invoked during this process [4]. Specifically: 1. Evaluation Order and Side Effects: The left operand is evaluated first [1]. If this evaluation causes a side effect (such as executing a function or getter), that side effect occurs before the right operand is evaluated. If the right operand evaluation then triggers an error, the side effect from the left operand has already occurred. 2. TheinOperator Specification: The operator requires the right-hand side to be an object [5][6]. During the runtime evaluation ofRelationalExpression: ShiftExpression in ShiftExpression, the specification mandates that the right operand must be converted to an object [4]. If the right operand is a primitive that cannot be converted to an object (such asnullorundefined), the engine throws a TypeError [5][7]. 3. The TypeError: The error "Cannot use 'in' operator" (or "invalid 'in' operand") occurs when the right-hand side is not an object [5][7]. Because the specification evaluates the left operand first, any side effects generated by that evaluation are not undone when the subsequent TypeError is thrown by the right-hand side [1]. In summary, theinoperator behaves as follows: - Left operand is evaluated [1]. - Right operand is evaluated [1]. - Right operand is checked to ensure it is an object [5][6]. - If the right operand check fails, a TypeError is thrown [5][7]. Any side effects from the earlier evaluation of the left operand are permanent [1].Citations:
🏁 Script executed:
Repository: PerryTS/perry
Length of output: 12254
🌐 Web query:
"({toString(){console.log('x')}} in 5)" Node V8 toString side effect💡 Result:
The expression ({toString{console.log('x')}} in 5) will indeed trigger the side effect in the toString method because the 'in' operator implicitly converts its left-hand operand to a property key [1][2]. When the 'in' operator is used, the JavaScript engine performs the following steps: 1. It evaluates the left-hand operand. In this case, it is an object literal with a custom toString method [3][2]. 2. The 'in' operator requires the left-hand operand to be a property key (a string or symbol) [1]. 3. To convert the object to a string, the engine invokes the object's toString method (or Symbol.toPrimitive if present) [3][4]. 4. Consequently, the console.log('x') inside the custom toString method is executed, printing 'x' to the console. In Node.js and other V8-based environments, this behavior is standard and strictly follows the ECMAScript specification for property key conversion [1][2]. The 'in' operator performs this conversion to ensure that the operand can be used as a key to check for the existence of a property on the right-hand object (in this case, the number 5, which is coerced to a wrapper object) [1][2]. Top results: [1][2]
Citations:
🌐 Web query:
V8 in operator toString side effect primitive right operand💡 Result:
The JavaScript
inoperator does not trigger atoStringside effect on its right-hand operand because it does not attempt to convert the right-hand operand to a string [1][2]. In fact, if the right-hand operand is a primitive, theinoperator will throw aTypeError[2][3]. According to the ECMAScript specification—and as implemented in V8—theinoperator requires the right-hand side to be an object (or aJSReceiverin V8's internal terminology) [1][2]. Specifically: 1. Theinoperator evaluates the left-hand operand, which is converted to a name (using theToPropertyKeyabstract operation, which does involveToStringorToPrimitivefor the key) [1]. 2. It then evaluates the right-hand operand. If the result is not an object, the engine throws aTypeError(e.g., "Cannot use 'in' operator to search for...") [1][3]. 3. Because the engine strictly enforces this object requirement, it never performs a conversion operation (such asToString) on the right-hand operand [1]. Therefore, there is no mechanism for theinoperator to trigger atoStringside effect on the object being searched, as it does not perform type coercion on that operand [1][2]. AnytoStringside effects related to theinoperator occur only on the left-hand side (the property key), as it must be coerced into a valid property name [1].Citations:
Avoid coercing object keys in the
inTypeError message —describe_in_operand(key)can run usertoString/Symbol.toPrimitiveon ordinary object keys, butToPropertyKeyforkey in rhshappens only after the RHS object check. Render object keys with a non-coercing fallback here to avoid side effects on the throw path.🤖 Prompt for AI Agents