feat(lint-unicorn): add rule prefer set has (#7075)

implementing the rule [prefer set
has](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-set-has.md)
from unicorn.

I put the fix as dangerous fix because they was no test for the fix on
the unicorn part. I did add some test for this but i'm not sure that i'm
covering everything.

---------

Co-authored-by: Boshen <boshenc@gmail.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
This commit is contained in:
jordan boyer 2024-11-25 12:33:51 +01:00 committed by GitHub
parent a68a2172d2
commit 79ab8cca0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 1183 additions and 0 deletions

View file

@ -361,6 +361,7 @@ mod unicorn {
pub mod prefer_query_selector;
pub mod prefer_reflect_apply;
pub mod prefer_regexp_test;
pub mod prefer_set_has;
pub mod prefer_set_size;
pub mod prefer_spread;
pub mod prefer_string_raw;
@ -954,6 +955,7 @@ oxc_macros::declare_all_lint_rules! {
unicorn::prefer_query_selector,
unicorn::prefer_reflect_apply,
unicorn::prefer_regexp_test,
unicorn::prefer_set_has,
unicorn::prefer_set_size,
unicorn::prefer_spread,
unicorn::prefer_string_raw,

View file

@ -0,0 +1,909 @@
use itertools::Itertools;
use oxc_ast::{
ast::{Expression, MemberExpression, VariableDeclarationKind},
AstKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::ScopeId;
use oxc_span::Span;
use phf::phf_set;
use crate::{ast_util::is_method_call, context::LintContext, rule::Rule, AstNode};
const ARRAY_METHODS_RETURNS_ARRAY: phf::Set<&'static str> = phf_set! {
"concat",
"copyWithin",
"fill",
"filter",
"flat",
"flatMap",
"map",
"reverse",
"slice",
"sort",
"splice",
"toReversed",
"toSorted",
"toSpliced",
"with",
};
fn prefer_set_has_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("should be a `Set`, and use `.has()` to check existence or non-existence.")
.with_help("Switch to `Set`")
.with_label(span)
}
#[derive(Debug, Default, Clone)]
pub struct PreferSetHas;
declare_oxc_lint!(
/// ### What it does
///
/// Prefer `Set#has()` over `Array#includes()` when checking for existence or non-existence.
///
/// ### Why is this bad?
///
/// Set#has() is faster than Array#includes().
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```js
/// const array = [1, 2, 3];
/// const hasValue = value => array.includes(value);
/// ```
///
/// Examples of **correct** code for this rule:
/// ```js
/// const set = new Set([1, 2, 3]);
/// const hasValue = value => set.has(value);
/// ```
/// ```js
/// const array = [1, 2, 3];
/// const hasOne = array.includes(1);
/// ```
PreferSetHas,
perf,
dangerous_fix
);
fn is_array_of_or_from(callee: &MemberExpression) -> bool {
callee.is_specific_member_access("Array", "of")
|| callee.is_specific_member_access("Array", "from")
}
fn is_kind_of_array_expr(expr: &Expression) -> bool {
match expr {
Expression::NewExpression(new_expr) => {
new_expr.callee.get_identifier_reference().map_or(false, |ident| ident.name == "Array")
}
Expression::CallExpression(call_expr) => {
let Some(callee) = call_expr.callee.get_member_expr() else {
return call_expr.callee_name().map_or(false, |name| name == "Array");
};
if callee.is_computed() || callee.optional() {
return false;
}
let Some(name) = callee.static_property_name() else { return false };
is_array_of_or_from(callee) || ARRAY_METHODS_RETURNS_ARRAY.contains(name)
}
Expression::ArrayExpression(_) => true,
_ => false,
}
}
fn is_multiple_calls(node: &AstNode, ctx: &LintContext, root_scope_id: ScopeId) -> bool {
let mut was_in_root_scope = node.scope_id() == root_scope_id;
let mut is_multiple = false;
for parent in ctx.nodes().ancestors(node.id()) {
let parent_scope = parent.scope_id();
if was_in_root_scope && parent_scope != root_scope_id {
is_multiple = false;
break;
}
was_in_root_scope = parent_scope == root_scope_id;
let parent_kind = parent.kind();
if matches!(
parent_kind,
AstKind::ForOfStatement(_)
| AstKind::ForInStatement(_)
| AstKind::ForStatement(_)
| AstKind::WhileStatement(_)
| AstKind::DoWhileStatement(_)
| AstKind::ArrowFunctionExpression(_)
| AstKind::Function(_)
) {
is_multiple = true;
break;
};
}
is_multiple
}
impl Rule for PreferSetHas {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::VariableDeclarator(declarator) = node.kind() else {
return;
};
if let VariableDeclarationKind::Var = declarator.kind {
return;
}
let Some(init) = declarator.init.as_ref() else {
return;
};
let is_kind_of_array = is_kind_of_array_expr(init);
if !is_kind_of_array {
return;
}
let Some(ident) = declarator.id.get_binding_identifier() else {
return;
};
let Some(symbol_id) = ident.symbol_id.get() else {
return;
};
let module_record = ctx.module_record();
if module_record.exported_bindings.contains_key(ident.name.as_str())
|| module_record.export_default.is_some_and(|default| default == ident.span)
{
return;
}
let symbol_table = ctx.symbols();
let mut references = symbol_table.get_resolved_references(symbol_id).peekable();
let Ok(len) = references.try_len() else {
return;
};
if len == 0 {
return;
}
let root_scope = node.scope_id();
if len == 1 {
let Some(reference) = references.peek() else {
return;
};
let node = ctx.nodes().get_node(reference.node_id());
if !is_multiple_calls(node, ctx, root_scope) {
return;
}
}
if references.any(|reference| {
let node = ctx.nodes().get_node(reference.node_id());
let Some(parent_id) = ctx.nodes().parent_id(node.id()) else {
return true;
};
let Some(AstKind::CallExpression(call_expression)) = ctx.nodes().parent_kind(parent_id)
else {
return true;
};
if call_expression.arguments.len() != 1 || call_expression.optional {
return true;
}
let arg = &call_expression.arguments[0];
if arg.is_spread() {
return true;
}
let Some(AstKind::MemberExpression(member_expr)) = ctx.nodes().parent_kind(node.id())
else {
return true;
};
if member_expr.optional() || member_expr.is_computed() {
return true;
}
let is_method = is_method_call(
call_expression,
Some(&[ident.name.as_str()]),
Some(&["includes"]),
Some(1),
Some(1),
);
!is_method
}) {
return;
}
ctx.diagnostic_with_fix(prefer_set_has_diagnostic(declarator.span), |fixer| {
let fixer = fixer.for_multifix();
let mut declaration_fix = fixer.new_fix_with_capacity(2);
let new_string = "new";
let set_start_string = "Set(";
let set_end_string = ")";
let set_array_string = format!("{set_start_string}[");
let net_set_array_string = format!("{new_string} {set_array_string}");
let new_set_start_string = format!("{new_string} {set_start_string}");
let array_end_string = format!("]{set_end_string}");
let array_parenthesis_len = 6; // Array( => 6
let new_space_len = 4; // new => 4
match init {
Expression::ArrayExpression(init_node) => {
declaration_fix
.push(fixer.insert_text_before(&init_node.span, new_set_start_string));
declaration_fix.push(fixer.insert_text_after(&init_node.span, set_end_string));
}
Expression::CallExpression(call_expr) => {
if call_expr.callee.is_identifier_reference() {
let start = call_expr.span.start;
let end = start + 6;
let span = Span::new(start, end);
declaration_fix.push(fixer.replace(span, net_set_array_string));
let start = call_expr.span.end - 1;
let end = start + 1;
let span = Span::new(start, end);
declaration_fix.push(fixer.replace(span, array_end_string));
} else {
declaration_fix
.push(fixer.insert_text_before(&call_expr.span, new_set_start_string));
declaration_fix
.push(fixer.insert_text_after(&call_expr.span, set_end_string));
}
}
Expression::NewExpression(new_expr) => {
let start = new_expr.span.start + new_space_len;
let end = start + array_parenthesis_len;
let span = Span::new(start, end);
declaration_fix.push(fixer.replace(span, set_array_string));
let start = new_expr.span.end - 1;
let end = start + 1;
let span = Span::new(start, end);
declaration_fix.push(fixer.replace(span, array_end_string));
}
_ => {}
}
let mut references_fix = fixer.new_fix_with_capacity(len);
let references = symbol_table.get_resolved_references(symbol_id);
for reference in references {
let node = ctx.nodes().get_node(reference.node_id());
let Some(parent) = ctx.nodes().parent_node(node.id()) else {
continue;
};
let AstKind::MemberExpression(member_expr) = parent.kind() else {
continue;
};
let Some(property_info) = member_expr.static_property_info() else {
continue;
};
references_fix.push(fixer.replace(property_info.0, "has"));
}
return declaration_fix.extend(references_fix);
});
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
"
const foo = new Set([1, 2, 3]);
function unicorn() {
return foo.has(1);
}
",
"
const foo = [1, 2, 3];
const isExists = foo.includes(1);
",
"
while (a) {
const foo = [1, 2, 3];
const isExists = foo.includes(1);
}
",
"
const foo = [1, 2, 3];
(() => {})(foo.includes(1));
",
"
foo = [1, 2, 3];
function unicorn() {
return foo.includes(1);
}
",
"
const exists = foo.includes(1);
",
"
const exists = [1, 2, 3].includes(1);
",
"
const foo = [1, 2, 3];
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes;
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return includes(foo);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return bar.includes(foo);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo[includes](1);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.indexOf(1) !== -1;
}
",
"
const foo = [1, 2, 3];
function unicorn() {
foo.includes(1);
foo.length = 1;
}
",
"
const foo = [1, 2, 3];
function unicorn() {
if (foo.includes(1)) {}
return foo;
}
",
"
var foo = [1, 2, 3];
var foo = [4, 5, 6];
function unicorn() {
return foo.includes(1);
}
",
"
const foo = bar;
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes();
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1, 1);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1, 0);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1, undefined);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(...[1]);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo?.includes(1);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes?.(1);
}
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo?.includes?.(1);
}
",
"
function unicorn() {
const foo = [1, 2, 3];
}
function unicorn2() {
return foo.includes(1);
}
",
"
export const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1);
}
",
"
module.exports = [1, 2, 3];
function unicorn() {
return module.exports.includes(1);
}
",
"
const foo = [1, 2, 3];
export {foo};
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
export default foo;
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
export {foo as bar};
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
module.exports = foo;
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
exports = foo;
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
module.exports.foo = foo;
function unicorn() {
return foo.includes(1);
}
",
"
const foo = NotArray(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = new NotArray(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = NotArray.from({length: 1}, (_, index) => index);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = NotArray.of(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = Array.notListed();
function unicorn() {
return foo.includes(1);
}
",
"
const foo = Array[from]({length: 1}, (_, index) => index);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = Array[of](1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = 'Array'.from({length: 1}, (_, index) => index);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = 'Array'.of(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = Array['from']({length: 1}, (_, index) => index);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = Array['of'](1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = of(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = from({length: 1}, (_, index) => index);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = bar.notListed();
function unicorn() {
return foo.includes(1);
}
",
"
const foo = _.map([1, 2, 3], value => value);
function unicorn() {
return _.includes(foo, 1);
}
",
"
@connect(
state => {
const availableComponents = ['is']
if (nsConfig.enabled) availableComponents.push('ns')
if (jsConfig.enabled) availableComponents.push('js')
if (asConfig.enabled) availableComponents.push('as')
return {
availableComponents,
}
},
)
export default class A {}
",
"
@connect(
state => {
const availableComponents = ['is']
if (nsConfig.enabled) availableComponents.push('ns')
if (jsConfig.enabled) availableComponents.push('js')
return {
availableComponents,
}
},
)
export default class A {}
",
];
let fail = vec![
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
const isExists = foo.includes(1);
const isExists2 = foo.includes(2);
",
"
const foo = [1, 2, 3];
for (const a of b) {
foo.includes(1);
}
",
"
async function unicorn() {
const foo = [1, 2, 3];
for await (const a of b) {
foo.includes(1);
}
}
",
"
const foo = [1, 2, 3];
for (let i = 0; i < n; i++) {
foo.includes(1);
}
",
"
const foo = [1, 2, 3];
for (let a in b) {
foo.includes(1);
}
",
"
const foo = [1, 2, 3];
while (a) {
foo.includes(1);
}
",
"
const foo = [1, 2, 3];
do {
foo.includes(1);
} while (a)
",
"
const foo = [1, 2, 3];
do {
// …
} while (foo.includes(1))
",
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
function * unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
async function unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
async function * unicorn() {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
const unicorn = function () {
return foo.includes(1);
}
",
"
const foo = [1, 2, 3];
const unicorn = () => foo.includes(1);
",
"
const foo = [1, 2, 3];
const a = {
b() {
return foo.includes(1);
}
};
",
"
const foo = [1, 2, 3];
class A {
b() {
return foo.includes(1);
}
}
",
"
const foo = [...bar];
function unicorn() {
return foo.includes(1);
}
bar.pop();
",
"
const foo = [1, 2, 3];
function unicorn() {
const exists = foo.includes(1);
function isExists(find) {
return foo.includes(find);
}
}
",
"
function wrap() {
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1);
}
}
const bar = [4, 5, 6];
function unicorn() {
return bar.includes(1);
}
",
"
const foo = [1, 2, 3];
function wrap() {
const exists = foo.includes(1);
const bar = [1, 2, 3];
function outer(find) {
const foo = [1, 2, 3];
while (a) {
foo.includes(1);
}
function inner(find) {
const bar = [1, 2, 3];
while (a) {
const exists = bar.includes(1);
}
}
}
}
",
"
const foo = Array(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = new Array(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = Array.from({length: 1}, (_, index) => index);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = Array.of(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = _([1,2,3]);
const bar = foo.map(value => value);
function unicorn() {
return bar.includes(1);
}
",
"
const a: Array<'foo' | 'bar'> = ['foo', 'bar']
for (let i = 0; i < 3; i++) {
if (a.includes(someString)) {
console.log(123)
}
}
",
];
let fix = vec![
(
"
const foo = [1, 2, 3];
function unicorn() {
return foo.includes(1);
}
",
"
const foo = new Set([1, 2, 3]);
function unicorn() {
return foo.has(1);
}
",
None,
),
(
"
const foo = [...bar];
function unicorn() {
return foo.includes(1);
}
bar.pop();
",
"
const foo = new Set([...bar]);
function unicorn() {
return foo.has(1);
}
bar.pop();
",
None,
),
(
"
const foo = Array(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = new Set([1, 2]);
function unicorn() {
return foo.has(1);
}
",
None,
),
(
"
const foo = new Array(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = new Set([1, 2]);
function unicorn() {
return foo.has(1);
}
",
None,
),
(
"
const foo = _([1,2,3]);
const bar = foo.map(value => value);
function unicorn() {
return bar.includes(1);
}
",
"
const foo = _([1,2,3]);
const bar = new Set(foo.map(value => value));
function unicorn() {
return bar.has(1);
}
",
None,
),
(
"
const foo = Array.of(1, 2);
function unicorn() {
return foo.includes(1);
}
",
"
const foo = new Set(Array.of(1, 2));
function unicorn() {
return foo.has(1);
}
",
None,
),
];
Tester::new(PreferSetHas::NAME, pass, fail).expect_fix(fix).test_and_snapshot();
}

View file

@ -0,0 +1,272 @@
---
source: crates/oxc_linter/src/tester.rs
---
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:10]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ const isExists = foo.includes(1);
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ for (const a of b) {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:3:20]
2 │ async function unicorn() {
3 │ const foo = [1, 2, 3];
· ───────────────
4 │ for await (const a of b) {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ for (let i = 0; i < n; i++) {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ for (let a in b) {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ while (a) {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ do {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ do {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ function * unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ async function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ async function * unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ const unicorn = function () {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ const unicorn = () => foo.includes(1);
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ const a = {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ class A {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [...bar];
· ──────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:3:20]
2 │ function wrap() {
3 │ const foo = [1, 2, 3];
· ───────────────
4 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:8:19]
7 │ }
8 │ const bar = [4, 5, 6];
· ───────────────
9 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = [1, 2, 3];
· ───────────────
3 │ function wrap() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:7:21]
6 │ function outer(find) {
7 │ const foo = [1, 2, 3];
· ───────────────
8 │ while (a) {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:12:22]
11 │ function inner(find) {
12 │ const bar = [1, 2, 3];
· ───────────────
13 │ while (a) {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = Array(1, 2);
· ─────────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = new Array(1, 2);
· ─────────────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = Array.from({length: 1}, (_, index) => index);
· ──────────────────────────────────────────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const foo = Array.of(1, 2);
· ────────────────────
3 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:3:19]
2 │ const foo = _([1,2,3]);
3 │ const bar = foo.map(value => value);
· ─────────────────────────────
4 │ function unicorn() {
╰────
help: Switch to `Set`
⚠ eslint-plugin-unicorn(prefer-set-has): should be a `Set`, and use `.has()` to check existence or non-existence.
╭─[prefer_set_has.tsx:2:19]
1 │
2 │ const a: Array<'foo' | 'bar'> = ['foo', 'bar']
· ────────────────────────────────────────
3 │
╰────
help: Switch to `Set`