forked from eslint/eslint
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new
no-new-native-nonconstructor
rule (eslint#16368)
* feat: add `no-new-noconstructor` rule * docs: add docs for `no-new-noconstructor` * fix: pass constructor name to message * docs: add `related_rules` * chore: add new rule * fix: rename `noconstructor` -> `nonconstructor` * fix: set `recommended: false` * fix: rename `no-new-nonconstructor` -> `no-new-native-nonconstructor` * docs: fix correct example * Update docs/src/rules/no-new-native-nonconstructor.md Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> * docs: update links * docs: address reviews * fix: `no constructor` -> `non-constructor` * Apply suggestions from code review Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com> Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com> Co-authored-by: Brandon Mills <btmills@users.noreply.github.com>
- Loading branch information
1 parent
978799b
commit f14587c
Showing
6 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
--- | ||
title: no-new-native-nonconstructor | ||
layout: doc | ||
rule_type: problem | ||
related_rules: | ||
- no-obj-calls | ||
further_reading: | ||
- https://tc39.es/ecma262/#sec-symbol-constructor | ||
- https://tc39.es/ecma262/#sec-bigint-constructor | ||
--- | ||
|
||
|
||
|
||
It is a convention in JavaScript that global variables beginning with an uppercase letter typically represent classes that can be instantiated using the `new` operator, such as `new Array` and `new Map`. Confusingly, JavaScript also provides some global variables that begin with an uppercase letter that cannot be called using the `new` operator and will throw an error if you attempt to do so. These are typically functions that are related to data types and are easy to mistake for classes. Consider the following example: | ||
|
||
```js | ||
// throws a TypeError | ||
let foo = new Symbol("foo"); | ||
|
||
// throws a TypeError | ||
let result = new BigInt(9007199254740991); | ||
|
||
Both `new Symbol` and `new BigInt` throw a type error because they are functions and not classes. It is easy to make this mistake by assuming the uppercase letters indicate classes. | ||
|
||
## Rule Details | ||
|
||
This rule is aimed at preventing the accidental calling of native JavaScript global functions with the `new` operator. These functions are: | ||
|
||
* `Symbol` | ||
* `BigInt` | ||
|
||
## Examples | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
::: incorrect | ||
|
||
```js | ||
/*eslint no-new-native-nonconstructor: "error"*/ | ||
/*eslint-env es2022*/ | ||
var foo = new Symbol('foo'); | ||
var bar = new BigInt(9007199254740991); | ||
``` | ||
|
||
::: | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
::: correct | ||
|
||
```js | ||
/*eslint no-new-native-nonconstructor: "error"*/ | ||
/*eslint-env es2022*/ | ||
var foo = Symbol('foo'); | ||
var bar = BigInt(9007199254740991); | ||
// Ignores shadowed Symbol. | ||
function baz(Symbol) { | ||
const qux = new Symbol("baz"); | ||
} | ||
function quux(BigInt) { | ||
const corge = new BigInt(9007199254740991); | ||
} | ||
``` | ||
|
||
::: | ||
|
||
## When Not To Use It | ||
|
||
This rule should not be used in ES3/5 environments. |
This file contains 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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/** | ||
* @fileoverview Rule to disallow use of the new operator with global non-constructor functions | ||
* @author Sosuke Suzuki | ||
*/ | ||
|
||
"use strict"; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
//------------------------------------------------------------------------------ | ||
|
||
const nonConstructorGlobalFunctionNames = ["Symbol", "BigInt"]; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
|
||
/** @type {import('../shared/types').Rule} */ | ||
module.exports = { | ||
meta: { | ||
type: "problem", | ||
|
||
docs: { | ||
description: "Disallow `new` operators with global non-constructor functions", | ||
recommended: false, | ||
url: "https://eslint.org/docs/rules/no-new-native-nonconstructor" | ||
}, | ||
|
||
schema: [], | ||
|
||
messages: { | ||
noNewNonconstructor: "`{{name}}` cannot be called as a constructor." | ||
} | ||
}, | ||
|
||
create(context) { | ||
|
||
return { | ||
"Program:exit"() { | ||
const globalScope = context.getScope(); | ||
|
||
for (const nonConstructorName of nonConstructorGlobalFunctionNames) { | ||
const variable = globalScope.set.get(nonConstructorName); | ||
|
||
if (variable && variable.defs.length === 0) { | ||
variable.references.forEach(ref => { | ||
const node = ref.identifier; | ||
const parent = node.parent; | ||
|
||
if (parent && parent.type === "NewExpression" && parent.callee === node) { | ||
context.report({ | ||
node, | ||
messageId: "noNewNonconstructor", | ||
data: { name: nonConstructorName } | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
} | ||
}; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/** | ||
* @fileoverview Tests for the no-new-native-nonconstructor rule | ||
* @author Sosuke Suzuki | ||
*/ | ||
|
||
"use strict"; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
|
||
const rule = require("../../../lib/rules/no-new-native-nonconstructor"), | ||
{ RuleTester } = require("../../../lib/rule-tester"); | ||
|
||
//------------------------------------------------------------------------------ | ||
// Tests | ||
//------------------------------------------------------------------------------ | ||
|
||
const ruleTester = new RuleTester({ env: { es2022: true } }); | ||
|
||
ruleTester.run("no-new-native-nonconstructor", rule, { | ||
valid: [ | ||
|
||
// Symbol | ||
"var foo = Symbol('foo');", | ||
"function bar(Symbol) { var baz = new Symbol('baz');}", | ||
"function Symbol() {} new Symbol();", | ||
"new foo(Symbol);", | ||
"new foo(bar, Symbol);", | ||
|
||
// BigInt | ||
"var foo = BigInt(9007199254740991);", | ||
"function bar(BigInt) { var baz = new BigInt(9007199254740991);}", | ||
"function BigInt() {} new BigInt();", | ||
"new foo(BigInt);", | ||
"new foo(bar, BigInt);" | ||
], | ||
invalid: [ | ||
|
||
// Symbol | ||
{ | ||
code: "var foo = new Symbol('foo');", | ||
errors: [{ | ||
message: "`Symbol` cannot be called as a constructor." | ||
}] | ||
}, | ||
{ | ||
code: "function bar() { return function Symbol() {}; } var baz = new Symbol('baz');", | ||
errors: [{ | ||
message: "`Symbol` cannot be called as a constructor." | ||
}] | ||
}, | ||
|
||
// BigInt | ||
{ | ||
code: "var foo = new BigInt(9007199254740991);", | ||
errors: [{ | ||
message: "`BigInt` cannot be called as a constructor." | ||
}] | ||
}, | ||
{ | ||
code: "function bar() { return function BigInt() {}; } var baz = new BigInt(9007199254740991);", | ||
errors: [{ | ||
message: "`BigInt` cannot be called as a constructor." | ||
}] | ||
} | ||
] | ||
}); |
This file contains 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