feat(linter): implement eslint/prefer-promise-reject-errors (#8254)

implement
https://eslint.org/docs/latest/rules/prefer-promise-reject-errors

(ref: #479 )
This commit is contained in:
tbashiyy 2025-01-13 23:25:51 +09:00 committed by GitHub
parent 25d4bf9aad
commit d178360a6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 597 additions and 100 deletions

View file

@ -7,6 +7,8 @@ use oxc_semantic::{AstNode, IsGlobalReference, NodeId, ReferenceId, Semantic, Sy
use oxc_span::{GetSpan, Span};
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator};
use crate::LintContext;
/// Test if an AST node is a boolean value that never changes. Specifically we
/// test for:
/// 1. Literal booleans (`true` or `false`)
@ -469,3 +471,89 @@ pub fn leftmost_identifier_reference<'a, 'b: 'a>(
_ => Err(expr),
}
}
fn is_definitely_non_error_type(ty: &TSType) -> bool {
match ty {
TSType::TSNumberKeyword(_)
| TSType::TSStringKeyword(_)
| TSType::TSBooleanKeyword(_)
| TSType::TSNullKeyword(_)
| TSType::TSUndefinedKeyword(_) => true,
TSType::TSUnionType(union) => union.types.iter().all(is_definitely_non_error_type),
TSType::TSIntersectionType(intersect) => {
intersect.types.iter().all(is_definitely_non_error_type)
}
_ => false,
}
}
pub fn could_be_error(ctx: &LintContext, expr: &Expression) -> bool {
match expr.get_inner_expression() {
Expression::NewExpression(_)
| Expression::AwaitExpression(_)
| Expression::CallExpression(_)
| Expression::ChainExpression(_)
| Expression::YieldExpression(_)
| Expression::PrivateFieldExpression(_)
| Expression::StaticMemberExpression(_)
| Expression::ComputedMemberExpression(_)
| Expression::TaggedTemplateExpression(_) => true,
Expression::AssignmentExpression(expr) => {
if matches!(expr.operator, AssignmentOperator::Assign | AssignmentOperator::LogicalAnd)
{
return could_be_error(ctx, &expr.right);
}
if matches!(
expr.operator,
AssignmentOperator::LogicalOr | AssignmentOperator::LogicalNullish
) {
return expr.left.get_expression().map_or(true, |expr| could_be_error(ctx, expr))
|| could_be_error(ctx, &expr.right);
}
false
}
Expression::SequenceExpression(expr) => {
expr.expressions.last().is_some_and(|expr| could_be_error(ctx, expr))
}
Expression::LogicalExpression(expr) => {
if matches!(expr.operator, LogicalOperator::And) {
return could_be_error(ctx, &expr.right);
}
could_be_error(ctx, &expr.left) || could_be_error(ctx, &expr.right)
}
Expression::ConditionalExpression(expr) => {
could_be_error(ctx, &expr.consequent) || could_be_error(ctx, &expr.alternate)
}
Expression::Identifier(ident) => {
let reference = ctx.symbols().get_reference(ident.reference_id());
let Some(symbol_id) = reference.symbol_id() else {
return true;
};
let decl = ctx.nodes().get_node(ctx.symbols().get_declaration(symbol_id));
match decl.kind() {
AstKind::VariableDeclarator(decl) => {
if let Some(init) = &decl.init {
could_be_error(ctx, init)
} else {
// TODO: warn about throwing undefined
false
}
}
AstKind::Function(_)
| AstKind::Class(_)
| AstKind::TSModuleDeclaration(_)
| AstKind::TSEnumDeclaration(_) => false,
AstKind::FormalParameter(param) => !param
.pattern
.type_annotation
.as_ref()
.is_some_and(|annot| is_definitely_non_error_type(&annot.type_annotation)),
_ => true,
}
}
_ => false,
}
}

View file

@ -146,6 +146,7 @@ mod eslint {
pub mod prefer_exponentiation_operator;
pub mod prefer_numeric_literals;
pub mod prefer_object_has_own;
pub mod prefer_promise_reject_errors;
pub mod prefer_rest_params;
pub mod prefer_spread;
pub mod radix;
@ -643,6 +644,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::no_var,
eslint::no_void,
eslint::no_with,
eslint::prefer_promise_reject_errors,
eslint::prefer_rest_params,
eslint::prefer_exponentiation_operator,
eslint::prefer_numeric_literals,

View file

@ -1,12 +1,9 @@
use oxc_ast::{
ast::{AssignmentOperator, Expression, LogicalOperator, TSType},
AstKind,
};
use oxc_ast::{ast::Expression, AstKind};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{ast_util::could_be_error, context::LintContext, rule::Rule, AstNode};
fn no_throw_literal_diagnostic(span: Span, is_undef: bool) -> OxcDiagnostic {
let message =
@ -92,7 +89,7 @@ impl Rule for NoThrowLiteral {
Expression::Identifier(id) if SPECIAL_IDENTIFIERS.contains(&id.name.as_str()) => {
ctx.diagnostic(no_throw_literal_diagnostic(expr.span(), true));
}
expr if !Self::could_be_error(ctx, expr) => {
expr if !could_be_error(ctx, expr) => {
ctx.diagnostic(no_throw_literal_diagnostic(expr.span(), false));
}
_ => {}
@ -100,100 +97,6 @@ impl Rule for NoThrowLiteral {
}
}
impl NoThrowLiteral {
fn could_be_error(ctx: &LintContext, expr: &Expression) -> bool {
match expr.get_inner_expression() {
Expression::NewExpression(_)
| Expression::AwaitExpression(_)
| Expression::CallExpression(_)
| Expression::ChainExpression(_)
| Expression::YieldExpression(_)
| Expression::PrivateFieldExpression(_)
| Expression::StaticMemberExpression(_)
| Expression::ComputedMemberExpression(_)
| Expression::TaggedTemplateExpression(_) => true,
Expression::AssignmentExpression(expr) => {
if matches!(
expr.operator,
AssignmentOperator::Assign | AssignmentOperator::LogicalAnd
) {
return Self::could_be_error(ctx, &expr.right);
}
if matches!(
expr.operator,
AssignmentOperator::LogicalOr | AssignmentOperator::LogicalNullish
) {
return expr
.left
.get_expression()
.map_or(true, |expr| Self::could_be_error(ctx, expr))
|| Self::could_be_error(ctx, &expr.right);
}
false
}
Expression::SequenceExpression(expr) => {
expr.expressions.last().is_some_and(|expr| Self::could_be_error(ctx, expr))
}
Expression::LogicalExpression(expr) => {
if matches!(expr.operator, LogicalOperator::And) {
return Self::could_be_error(ctx, &expr.right);
}
Self::could_be_error(ctx, &expr.left) || Self::could_be_error(ctx, &expr.right)
}
Expression::ConditionalExpression(expr) => {
Self::could_be_error(ctx, &expr.consequent)
|| Self::could_be_error(ctx, &expr.alternate)
}
Expression::Identifier(ident) => {
let reference = ctx.symbols().get_reference(ident.reference_id());
let Some(symbol_id) = reference.symbol_id() else {
return true;
};
let decl = ctx.nodes().get_node(ctx.symbols().get_declaration(symbol_id));
match decl.kind() {
AstKind::VariableDeclarator(decl) => {
if let Some(init) = &decl.init {
Self::could_be_error(ctx, init)
} else {
// TODO: warn about throwing undefined
false
}
}
AstKind::Function(_)
| AstKind::Class(_)
| AstKind::TSModuleDeclaration(_)
| AstKind::TSEnumDeclaration(_) => false,
AstKind::FormalParameter(param) => {
!param.pattern.type_annotation.as_ref().is_some_and(|annot| {
is_definitely_non_error_type(&annot.type_annotation)
})
}
_ => true,
}
}
_ => false,
}
}
}
fn is_definitely_non_error_type(ty: &TSType) -> bool {
match ty {
TSType::TSNumberKeyword(_)
| TSType::TSStringKeyword(_)
| TSType::TSBooleanKeyword(_)
| TSType::TSNullKeyword(_)
| TSType::TSUndefinedKeyword(_) => true,
TSType::TSUnionType(union) => union.types.iter().all(is_definitely_non_error_type),
TSType::TSIntersectionType(intersect) => {
intersect.types.iter().all(is_definitely_non_error_type)
}
_ => false,
}
}
#[test]
fn test() {
use crate::tester::Tester;

View file

@ -0,0 +1,262 @@
use oxc_allocator::Box;
use oxc_ast::{
ast::{Argument, CallExpression, Expression, FormalParameters},
AstKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{
ast_util::{could_be_error, is_method_call},
context::LintContext,
rule::Rule,
AstNode,
};
fn prefer_promise_reject_errors_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Expected the Promise rejection reason to be an Error").with_label(span)
}
#[derive(Debug, Default, Clone)]
pub struct PreferPromiseRejectErrors {
allow_empty_reject: bool,
}
declare_oxc_lint!(
/// ### What it does
///
/// Require using Error objects as Promise rejection reasons
///
/// ### Why is this bad?
///
/// It is considered good practice to only pass instances of the built-in `Error` object to the `reject()` function for user-defined errors in Promises. `Error` objects automatically store a stack trace, which can be used to debug an error by determining where it came from. If a Promise is rejected with a non-`Error` value, it can be difficult to determine where the rejection occurred.
///
/// ### Options
///
/// This rule takes one optional object argument:
/// - `allowEmptyReject: true` (`false` by default) allows calls to `Promise.reject()` with no arguments.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```js
/// Promise.reject("something bad happened");
///
/// Promise.reject(5);
///
/// Promise.reject();
///
/// new Promise(function(resolve, reject) {
/// reject("something bad happened")
/// });
///
/// new Promise(function(resolve, reject) {
/// reject();
/// });
/// ```
///
/// Examples of **correct** code for this rule:
/// ```js
/// Promise.reject(new Error("something bad happened"));
///
/// Promise.reject(new TypeError("something bad happened"));
///
/// new Promise(function(resolve, reject) {
/// reject(new Error("something bad happened"));
/// });
///
/// var foo = getUnknownValue();
/// Promise.reject(foo);
/// ```
PreferPromiseRejectErrors,
eslint,
style,
none
);
impl Rule for PreferPromiseRejectErrors {
fn from_configuration(value: serde_json::Value) -> Self {
let allow_empty_reject = value.get(0).is_some_and(|v| {
v.get("allowEmptyReject").is_some_and(|b| b.as_bool().unwrap_or(false))
});
Self { allow_empty_reject }
}
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.kind() {
AstKind::CallExpression(call_expr) => {
if !is_method_call(call_expr, Some(&["Promise"]), Some(&["reject"]), None, None) {
return;
}
check_reject_call(call_expr, ctx, self.allow_empty_reject);
}
AstKind::NewExpression(new_expr) => {
let Expression::Identifier(ident) = &new_expr.callee else {
return;
};
if ident.name != "Promise" || new_expr.arguments.len() == 0 {
return;
}
let Some(arg) =
new_expr.arguments[0].as_expression().map(Expression::get_inner_expression)
else {
return;
};
match arg {
Expression::FunctionExpression(func) => {
check_reject_in_function(&func.params, ctx, self.allow_empty_reject);
}
Expression::ArrowFunctionExpression(func) => {
check_reject_in_function(&func.params, ctx, self.allow_empty_reject);
}
_ => {}
}
}
_ => {}
}
}
}
fn check_reject_call(call_expr: &CallExpression, ctx: &LintContext, allow_empty_reject: bool) {
if call_expr.arguments.len() == 0 && allow_empty_reject {
return;
}
if call_expr.arguments.len() == 0
|| call_expr.arguments[0].as_expression().is_some_and(|e| !could_be_error(ctx, e))
|| is_undefined(&call_expr.arguments[0])
{
ctx.diagnostic(prefer_promise_reject_errors_diagnostic(call_expr.span));
}
}
fn check_reject_in_function(
params: &Box<'_, FormalParameters<'_>>,
ctx: &LintContext,
allow_empty_reject: bool,
) {
if params.parameters_count() <= 1 {
return;
}
let Some(reject_arg) = params.items[1].pattern.get_binding_identifier() else {
return;
};
ctx.symbol_references(reject_arg.symbol_id()).for_each(|reference| {
let Some(node) = ctx.nodes().parent_node(reference.node_id()) else {
return;
};
if let AstKind::CallExpression(call_expr) = node.kind() {
check_reject_call(call_expr, ctx, allow_empty_reject);
}
});
}
fn is_undefined(arg: &Argument) -> bool {
match arg.as_expression().map(oxc_ast::ast::Expression::get_inner_expression) {
Some(Expression::Identifier(ident)) => ident.name == "undefined",
_ => false,
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
("Promise.resolve(5)", None),
("Foo.reject(5)", None),
("Promise.reject(foo)", None),
("Promise.reject(foo.bar)", None),
("Promise.reject(foo.bar())", None),
("Promise.reject(new Error())", None),
("Promise.reject(new TypeError)", None),
("Promise.reject(new Error('foo'))", None),
("Promise.reject(foo || 5)", None),
("Promise.reject(5 && foo)", None),
("new Foo((resolve, reject) => reject(5))", None),
("new Promise(function(resolve, reject) { return function(reject) { reject(5) } })", None),
("new Promise(function(resolve, reject) { if (foo) { const reject = somethingElse; reject(5) } })", None),
("new Promise(function(resolve, {apply}) { apply(5) })", None),
("new Promise(function(resolve, reject) { resolve(5, reject) })", None),
("async function foo() { Promise.reject(await foo); }", None),
("Promise.reject()", Some(serde_json::json!([{ "allowEmptyReject": true }]))),
("new Promise(function(resolve, reject) { reject() })", Some(serde_json::json!([{ "allowEmptyReject": true }]))),
("Promise.reject(obj?.foo)", None),
("Promise.reject(obj?.foo())", None),
("Promise.reject(foo = new Error())", None),
("Promise.reject(foo ||= 5)", None),
("Promise.reject(foo.bar ??= 5)", None),
("Promise.reject(foo[bar] ??= 5)", None),
("class C { #reject; foo() { Promise.#reject(5); } }", None),
("class C { #error; foo() { Promise.reject(this.#error); } }", None)
];
let fail = vec![
("Promise.reject(5)", None),
("Promise.reject('foo')", None),
("Promise.reject(`foo`)", None),
("Promise.reject(!foo)", None),
("Promise.reject(void foo)", None),
("Promise.reject()", None),
("Promise.reject(undefined)", None),
("Promise.reject({ foo: 1 })", None),
("Promise.reject([1, 2, 3])", None),
("Promise.reject()", Some(serde_json::json!([{ "allowEmptyReject": false }]))),
(
"new Promise(function(resolve, reject) { reject() })",
Some(serde_json::json!([{ "allowEmptyReject": false }])),
),
("Promise.reject(undefined)", Some(serde_json::json!([{ "allowEmptyReject": true }]))),
("Promise.reject('foo', somethingElse)", None),
("new Promise(function(resolve, reject) { reject(5) })", None),
("new Promise((resolve, reject) => { reject(5) })", None),
("new Promise((resolve, reject) => reject(5))", None),
("new Promise((resolve, reject) => reject())", None),
("new Promise(function(yes, no) { no(5) })", None),
(
"
new Promise((resolve, reject) => {
fs.readFile('foo.txt', (err, file) => {
if (err) reject('File not found')
else resolve(file)
})
})
",
None,
),
("new Promise(({foo, bar, baz}, reject) => reject(5))", None),
("new Promise(function(reject, reject) { reject(5) })", None),
("new Promise(function(foo, arguments) { arguments(5) })", None),
("new Promise((foo, arguments) => arguments(5))", None),
("new Promise(function({}, reject) { reject(5) })", None),
("new Promise(({}, reject) => reject(5))", None),
("new Promise((resolve, reject, somethingElse = reject(5)) => {})", None),
// Optional chaining
("Promise.reject?.(5)", None),
("Promise?.reject(5)", None),
("Promise?.reject?.(5)", None),
("(Promise?.reject)(5)", None),
("(Promise?.reject)?.(5)", None),
// Assignments with mathematical operators will either evaluate to a primitive value or throw a TypeError
("Promise.reject(foo += new Error())", None),
("Promise.reject(foo -= new Error())", None),
("Promise.reject(foo **= new Error())", None),
("Promise.reject(foo <<= new Error())", None),
("Promise.reject(foo |= new Error())", None),
("Promise.reject(foo &= new Error())", None),
// evaluates either to a falsy value of `foo` (which, then, cannot be an Error object), or to `5`
("Promise.reject(foo && 5)", None),
("Promise.reject(foo &&= 5)", None),
];
Tester::new(PreferPromiseRejectErrors::NAME, PreferPromiseRejectErrors::PLUGIN, pass, fail)
.test_and_snapshot();
}

View file

@ -0,0 +1,242 @@
---
source: crates/oxc_linter/src/tester.rs
assertion_line: 356
snapshot_kind: text
---
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(5)
· ─────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject('foo')
· ─────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(`foo`)
· ─────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(!foo)
· ────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(void foo)
· ────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject()
· ────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(undefined)
· ─────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject({ foo: 1 })
· ──────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject([1, 2, 3])
· ─────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject()
· ────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:41]
1 │ new Promise(function(resolve, reject) { reject() })
· ────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(undefined)
· ─────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject('foo', somethingElse)
· ────────────────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:41]
1 │ new Promise(function(resolve, reject) { reject(5) })
· ─────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:36]
1 │ new Promise((resolve, reject) => { reject(5) })
· ─────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:34]
1 │ new Promise((resolve, reject) => reject(5))
· ─────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:34]
1 │ new Promise((resolve, reject) => reject())
· ────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:33]
1 │ new Promise(function(yes, no) { no(5) })
· ─────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:4:26]
3 │ fs.readFile('foo.txt', (err, file) => {
4 │ if (err) reject('File not found')
· ────────────────────────
5 │ else resolve(file)
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:42]
1 │ new Promise(({foo, bar, baz}, reject) => reject(5))
· ─────────
╰────
× Identifier `reject` has already been declared
╭─[prefer_promise_reject_errors.tsx:1:22]
1 │ new Promise(function(reject, reject) { reject(5) })
· ───┬── ───┬──
· │ ╰── It can not be redeclared here
· ╰── `reject` has already been declared here
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:40]
1 │ new Promise(function(foo, arguments) { arguments(5) })
· ────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:33]
1 │ new Promise((foo, arguments) => arguments(5))
· ────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:36]
1 │ new Promise(function({}, reject) { reject(5) })
· ─────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:29]
1 │ new Promise(({}, reject) => reject(5))
· ─────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:47]
1 │ new Promise((resolve, reject, somethingElse = reject(5)) => {})
· ─────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject?.(5)
· ───────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise?.reject(5)
· ──────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise?.reject?.(5)
· ────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ (Promise?.reject)(5)
· ────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ (Promise?.reject)?.(5)
· ──────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo += new Error())
· ──────────────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo -= new Error())
· ──────────────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo **= new Error())
· ───────────────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo <<= new Error())
· ───────────────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo |= new Error())
· ──────────────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo &= new Error())
· ──────────────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo && 5)
· ────────────────────────
╰────
⚠ eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
1 │ Promise.reject(foo &&= 5)
· ─────────────────────────
╰────