2 * @fileoverview Rule to disallow unnecessary computed property keys in object literals
3 * @author Burak Yigit Kaya
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const astUtils
= require("./utils/ast-utils");
13 //------------------------------------------------------------------------------
15 //------------------------------------------------------------------------------
18 * Determines whether the computed key syntax is unnecessarily used for the given node.
19 * In particular, it determines whether removing the square brackets and using the content between them
20 * directly as the key (e.g. ['foo'] -> 'foo') would produce valid syntax and preserve the same behavior.
21 * Valid non-computed keys are only: identifiers, number literals and string literals.
22 * Only literals can preserve the same behavior, with a few exceptions for specific node types:
24 * - { ["__proto__"]: foo } defines a property named "__proto__"
25 * { "__proto__": foo } defines object's prototype
27 * - class C { ["constructor"]; } defines an instance field named "constructor"
28 * class C { "constructor"; } produces a parsing error
29 * - class C { static ["constructor"]; } defines a static field named "constructor"
30 * class C { static "constructor"; } produces a parsing error
31 * - class C { static ["prototype"]; } produces a runtime error (doesn't break the whole script)
32 * class C { static "prototype"; } produces a parsing error (breaks the whole script)
34 * - class C { ["constructor"]() {} } defines a prototype method named "constructor"
35 * class C { "constructor"() {} } defines the constructor
36 * - class C { static ["prototype"]() {} } produces a runtime error (doesn't break the whole script)
37 * class C { static "prototype"() {} } produces a parsing error (breaks the whole script)
38 * @param {ASTNode} node The node to check. It can be `Property`, `PropertyDefinition` or `MethodDefinition`.
39 * @throws {Error} (Unreachable.)
40 * @returns {void} `true` if the node has useless computed key.
42 function hasUselessComputedKey(node
) {
49 if (key
.type
!== "Literal") {
53 const { value
} = key
;
55 if (typeof value
!== "number" && typeof value
!== "string") {
61 return value
!== "__proto__";
63 case "PropertyDefinition":
65 return value
!== "constructor" && value
!== "prototype";
68 return value
!== "constructor";
70 case "MethodDefinition":
72 return value
!== "prototype";
75 return value
!== "constructor";
77 /* istanbul ignore next */
79 throw new Error(`Unexpected node type: ${node.type}`);
84 //------------------------------------------------------------------------------
86 //------------------------------------------------------------------------------
93 description
: "disallow unnecessary computed property keys in objects and classes",
95 url
: "https://eslint.org/docs/rules/no-useless-computed-key"
101 enforceForClassMembers
: {
106 additionalProperties
: false
111 unnecessarilyComputedProperty
: "Unnecessarily computed property [{{property}}] found."
115 const sourceCode
= context
.getSourceCode();
116 const enforceForClassMembers
= context
.options
[0] && context
.options
[0].enforceForClassMembers
;
119 * Reports a given node if it violated this rule.
120 * @param {ASTNode} node The node to check.
123 function check(node
) {
124 if (hasUselessComputedKey(node
)) {
125 const { key
} = node
;
129 messageId
: "unnecessarilyComputedProperty",
130 data
: { property
: sourceCode
.getText(key
) },
132 const leftSquareBracket
= sourceCode
.getTokenBefore(key
, astUtils
.isOpeningBracketToken
);
133 const rightSquareBracket
= sourceCode
.getTokenAfter(key
, astUtils
.isClosingBracketToken
);
135 // If there are comments between the brackets and the property name, don't do a fix.
136 if (sourceCode
.commentsExistBetween(leftSquareBracket
, rightSquareBracket
)) {
140 const tokenBeforeLeftBracket
= sourceCode
.getTokenBefore(leftSquareBracket
);
142 // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} })
143 const needsSpaceBeforeKey
= tokenBeforeLeftBracket
.range
[1] === leftSquareBracket
.range
[0] &&
144 !astUtils
.canTokensBeAdjacent(tokenBeforeLeftBracket
, sourceCode
.getFirstToken(key
));
146 const replacementKey
= (needsSpaceBeforeKey
? " " : "") + key
.raw
;
148 return fixer
.replaceTextRange([leftSquareBracket
.range
[0], rightSquareBracket
.range
[1]], replacementKey
);
155 * A no-op function to act as placeholder for checking a node when the `enforceForClassMembers` option is `false`.
163 MethodDefinition
: enforceForClassMembers
? check
: noop
,
164 PropertyDefinition
: enforceForClassMembers
? check
: noop