Skip to content

Commit 22a5582

Browse files
feat: add rule no-object-constructor, deprecate no-new-object (#17576)
* Deprecate `no-new-object` * Add rule `no-object-constructor` * Apply suggestions * mark no-new-object deprecated in v8.50.0 Co-authored-by: Francesco Trotta <[email protected]> --------- Co-authored-by: Milos Djermanovic <[email protected]>
1 parent 48a44a7 commit 22a5582

12 files changed

Lines changed: 323 additions & 13 deletions

File tree

docs/src/_data/rules.json

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -950,13 +950,6 @@
950950
"fixable": false,
951951
"hasSuggestions": false
952952
},
953-
{
954-
"name": "no-new-object",
955-
"description": "Disallow `Object` constructors",
956-
"recommended": false,
957-
"fixable": false,
958-
"hasSuggestions": false
959-
},
960953
{
961954
"name": "no-new-wrappers",
962955
"description": "Disallow `new` operators with the `String`, `Number`, and `Boolean` objects",
@@ -971,6 +964,13 @@
971964
"fixable": false,
972965
"hasSuggestions": true
973966
},
967+
{
968+
"name": "no-object-constructor",
969+
"description": "Disallow calls to the `Object` constructor without an argument",
970+
"recommended": false,
971+
"fixable": false,
972+
"hasSuggestions": true
973+
},
974974
{
975975
"name": "no-octal",
976976
"description": "Disallow octal literals",
@@ -1956,6 +1956,12 @@
19561956
"no-unsafe-negation"
19571957
]
19581958
},
1959+
{
1960+
"name": "no-new-object",
1961+
"replacedBy": [
1962+
"no-object-constructor"
1963+
]
1964+
},
19591965
{
19601966
"name": "no-new-require",
19611967
"replacedBy": []

docs/src/_data/rules_meta.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,11 @@
13641364
"description": "Disallow `Object` constructors",
13651365
"recommended": false,
13661366
"url": "https://eslint.org/docs/latest/rules/no-new-object"
1367-
}
1367+
},
1368+
"deprecated": true,
1369+
"replacedBy": [
1370+
"no-object-constructor"
1371+
]
13681372
},
13691373
"no-new-require": {
13701374
"deprecated": true,
@@ -1409,6 +1413,15 @@
14091413
"url": "https://eslint.org/docs/latest/rules/no-obj-calls"
14101414
}
14111415
},
1416+
"no-object-constructor": {
1417+
"type": "suggestion",
1418+
"docs": {
1419+
"description": "Disallow calls to the `Object` constructor without an argument",
1420+
"recommended": false,
1421+
"url": "https://eslint.org/docs/latest/rules/no-object-constructor"
1422+
},
1423+
"hasSuggestions": true
1424+
},
14121425
"no-octal": {
14131426
"type": "suggestion",
14141427
"docs": {

docs/src/rules/no-array-constructor.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
title: no-array-constructor
33
rule_type: suggestion
44
related_rules:
5-
- no-new-object
65
- no-new-wrappers
6+
- no-object-constructor
77
---
88

99

docs/src/rules/no-new-object.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ related_rules:
66
- no-new-wrappers
77
---
88

9+
This rule was **deprecated** in ESLint v8.50.0 and replaced by the [no-object-constructor](no-object-constructor) rule. The new rule flags more situations where object literal syntax can be used, and it does not report a problem when the `Object` constructor is invoked with an argument.
910

1011
The `Object` constructor is used to create new generic objects in JavaScript, such as:
1112

@@ -25,7 +26,7 @@ While there are no performance differences between the two approaches, the byte
2526

2627
## Rule Details
2728

28-
This rule disallows `Object` constructors.
29+
This rule disallows calling the `Object` constructor with `new`.
2930

3031
Examples of **incorrect** code for this rule:
3132

@@ -37,6 +38,8 @@ Examples of **incorrect** code for this rule:
3738
var myObject = new Object();
3839

3940
new Object();
41+
42+
var foo = new Object("foo");
4043
```
4144

4245
:::
@@ -54,10 +57,12 @@ var myObject = {};
5457

5558
var Object = function Object() {};
5659
new Object();
60+
61+
var foo = Object("foo");
5762
```
5863

5964
:::
6065

6166
## When Not To Use It
6267

63-
If you wish to allow the use of the `Object` constructor, you can safely turn this rule off.
68+
If you wish to allow the use of the `Object` constructor with `new`, you can safely turn this rule off.

docs/src/rules/no-new-wrappers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: no-new-wrappers
33
rule_type: suggestion
44
related_rules:
55
- no-array-constructor
6-
- no-new-object
6+
- no-object-constructor
77
further_reading:
88
- https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-3/wrapper-objects
99
---
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
title: no-object-constructor
3+
rule_type: suggestion
4+
related_rules:
5+
- no-array-constructor
6+
- no-new-wrappers
7+
---
8+
9+
Use of the `Object` constructor to construct a new empty object is generally discouraged in favor of object literal notation because of conciseness and because the `Object` global may be redefined.
10+
The exception is when the `Object` constructor is used to intentionally wrap a specified value which is passed as an argument.
11+
12+
## Rule Details
13+
14+
This rule disallows calling the `Object` constructor without an argument.
15+
16+
Examples of **incorrect** code for this rule:
17+
18+
:::incorrect
19+
20+
```js
21+
/*eslint no-object-constructor: "error"*/
22+
23+
Object();
24+
25+
new Object();
26+
```
27+
28+
:::
29+
30+
Examples of **correct** code for this rule:
31+
32+
:::correct
33+
34+
```js
35+
/*eslint no-object-constructor: "error"*/
36+
37+
Object("foo");
38+
39+
const obj = { a: 1, b: 2 };
40+
41+
const isObject = value => value === Object(value);
42+
43+
const createObject = Object => new Object();
44+
```
45+
46+
:::
47+
48+
## When Not To Use It
49+
50+
If you wish to allow the use of the `Object` constructor, you can safely turn this rule off.

lib/rules/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
175175
"no-new-wrappers": () => require("./no-new-wrappers"),
176176
"no-nonoctal-decimal-escape": () => require("./no-nonoctal-decimal-escape"),
177177
"no-obj-calls": () => require("./no-obj-calls"),
178+
"no-object-constructor": () => require("./no-object-constructor"),
178179
"no-octal": () => require("./no-octal"),
179180
"no-octal-escape": () => require("./no-octal-escape"),
180181
"no-param-reassign": () => require("./no-param-reassign"),

lib/rules/no-new-object.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* @fileoverview A rule to disallow calls to the Object constructor
33
* @author Matt DuVall <http://www.mattduvall.com/>
4+
* @deprecated in ESLint v8.50.0
45
*/
56

67
"use strict";
@@ -26,6 +27,12 @@ module.exports = {
2627
url: "https://eslint.org/docs/latest/rules/no-new-object"
2728
},
2829

30+
deprecated: true,
31+
32+
replacedBy: [
33+
"no-object-constructor"
34+
],
35+
2936
schema: [],
3037

3138
messages: {

lib/rules/no-object-constructor.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* @fileoverview Rule to disallow calls to the `Object` constructor without an argument
3+
* @author Francesco Trotta
4+
*/
5+
6+
"use strict";
7+
8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
const { getVariableByName, isArrowToken } = require("./utils/ast-utils");
13+
14+
//------------------------------------------------------------------------------
15+
// Helpers
16+
//------------------------------------------------------------------------------
17+
18+
/**
19+
* Tests if a node appears at the beginning of an ancestor ExpressionStatement node.
20+
* @param {ASTNode} node The node to check.
21+
* @returns {boolean} Whether the node appears at the beginning of an ancestor ExpressionStatement node.
22+
*/
23+
function isStartOfExpressionStatement(node) {
24+
const start = node.range[0];
25+
let ancestor = node;
26+
27+
while ((ancestor = ancestor.parent) && ancestor.range[0] === start) {
28+
if (ancestor.type === "ExpressionStatement") {
29+
return true;
30+
}
31+
}
32+
return false;
33+
}
34+
35+
//------------------------------------------------------------------------------
36+
// Rule Definition
37+
//------------------------------------------------------------------------------
38+
39+
/** @type {import('../shared/types').Rule} */
40+
module.exports = {
41+
meta: {
42+
type: "suggestion",
43+
44+
docs: {
45+
description: "Disallow calls to the `Object` constructor without an argument",
46+
recommended: false,
47+
url: "https://eslint.org/docs/latest/rules/no-object-constructor"
48+
},
49+
50+
hasSuggestions: true,
51+
52+
schema: [],
53+
54+
messages: {
55+
preferLiteral: "The object literal notation {} is preferable.",
56+
useLiteral: "Replace with '{{replacement}}'."
57+
}
58+
},
59+
60+
create(context) {
61+
62+
const sourceCode = context.sourceCode;
63+
64+
/**
65+
* Determines whether or not an object literal that replaces a specified node needs to be enclosed in parentheses.
66+
* @param {ASTNode} node The node to be replaced.
67+
* @returns {boolean} Whether or not parentheses around the object literal are required.
68+
*/
69+
function needsParentheses(node) {
70+
if (isStartOfExpressionStatement(node)) {
71+
return true;
72+
}
73+
74+
const prevToken = sourceCode.getTokenBefore(node);
75+
76+
if (prevToken && isArrowToken(prevToken)) {
77+
return true;
78+
}
79+
80+
return false;
81+
}
82+
83+
/**
84+
* Reports on nodes where the `Object` constructor is called without arguments.
85+
* @param {ASTNode} node The node to evaluate.
86+
* @returns {void}
87+
*/
88+
function check(node) {
89+
if (node.callee.type !== "Identifier" || node.callee.name !== "Object" || node.arguments.length) {
90+
return;
91+
}
92+
93+
const variable = getVariableByName(sourceCode.getScope(node), "Object");
94+
95+
if (variable && variable.identifiers.length === 0) {
96+
const replacement = needsParentheses(node) ? "({})" : "{}";
97+
98+
context.report({
99+
node,
100+
messageId: "preferLiteral",
101+
suggest: [
102+
{
103+
messageId: "useLiteral",
104+
data: { replacement },
105+
fix: fixer => fixer.replaceText(node, replacement)
106+
}
107+
]
108+
});
109+
}
110+
}
111+
112+
return {
113+
CallExpression: check,
114+
NewExpression: check
115+
};
116+
117+
}
118+
};

packages/js/src/configs/eslint-all.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,11 @@ module.exports = Object.freeze({
152152
"no-new": "error",
153153
"no-new-func": "error",
154154
"no-new-native-nonconstructor": "error",
155-
"no-new-object": "error",
156155
"no-new-symbol": "error",
157156
"no-new-wrappers": "error",
158157
"no-nonoctal-decimal-escape": "error",
159158
"no-obj-calls": "error",
159+
"no-object-constructor": "error",
160160
"no-octal": "error",
161161
"no-octal-escape": "error",
162162
"no-param-reassign": "error",

0 commit comments

Comments
 (0)