Skip to content

Commit 57aa6c7

Browse files
fix(eslint-plugin): [no-throw-literal] false positive with logical expressions (typescript-eslint#2645)
1 parent 8e44c78 commit 57aa6c7

File tree

2 files changed

+49
-60
lines changed

2 files changed

+49
-60
lines changed

packages/eslint-plugin/src/rules/no-throw-literal.ts

Lines changed: 13 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -61,40 +61,6 @@ export default util.createRule({
6161
return false;
6262
}
6363

64-
function tryGetThrowArgumentType(node: TSESTree.Node): ts.Type | null {
65-
switch (node.type) {
66-
case AST_NODE_TYPES.Identifier:
67-
case AST_NODE_TYPES.CallExpression:
68-
case AST_NODE_TYPES.NewExpression:
69-
case AST_NODE_TYPES.MemberExpression:
70-
case AST_NODE_TYPES.TSAsExpression: {
71-
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
72-
return checker.getTypeAtLocation(tsNode);
73-
}
74-
75-
case AST_NODE_TYPES.AssignmentExpression:
76-
return tryGetThrowArgumentType(node.right);
77-
78-
case AST_NODE_TYPES.SequenceExpression:
79-
return tryGetThrowArgumentType(
80-
node.expressions[node.expressions.length - 1],
81-
);
82-
83-
case AST_NODE_TYPES.LogicalExpression: {
84-
const left = tryGetThrowArgumentType(node.left);
85-
return left ?? tryGetThrowArgumentType(node.right);
86-
}
87-
88-
case AST_NODE_TYPES.ConditionalExpression: {
89-
const consequent = tryGetThrowArgumentType(node.consequent);
90-
return consequent ?? tryGetThrowArgumentType(node.alternate);
91-
}
92-
93-
default:
94-
return null;
95-
}
96-
}
97-
9864
function checkThrowArgument(node: TSESTree.Node): void {
9965
if (
10066
node.type === AST_NODE_TYPES.AwaitExpression ||
@@ -103,20 +69,20 @@ export default util.createRule({
10369
return;
10470
}
10571

106-
const type = tryGetThrowArgumentType(node);
107-
if (type) {
108-
if (type.flags & ts.TypeFlags.Undefined) {
109-
context.report({ node, messageId: 'undef' });
110-
return;
111-
}
72+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
73+
const type = checker.getTypeAtLocation(tsNode);
11274

113-
if (
114-
util.isTypeAnyType(type) ||
115-
util.isTypeUnknownType(type) ||
116-
isErrorLike(type)
117-
) {
118-
return;
119-
}
75+
if (type.flags & ts.TypeFlags.Undefined) {
76+
context.report({ node, messageId: 'undef' });
77+
return;
78+
}
79+
80+
if (
81+
util.isTypeAnyType(type) ||
82+
util.isTypeUnknownType(type) ||
83+
isErrorLike(type)
84+
) {
85+
return;
12086
}
12187

12288
context.report({ node, messageId: 'object' });

packages/eslint-plugin/tests/rules/no-throw-literal.test.ts

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,13 @@ throw new CustomError();
6161
`
6262
class CustomError1 extends Error {}
6363
class CustomError2 extends CustomError1 {}
64-
throw new CustomError();
64+
throw new CustomError2();
6565
`,
6666
'throw (foo = new Error());',
6767
'throw (1, 2, new Error());',
6868
"throw 'literal' && new Error();",
6969
"throw new Error() || 'literal';",
70-
"throw foo ? new Error() : 'literal';",
71-
"throw foo ? 'literal' : new Error();",
70+
'throw foo ? new Error() : new Error();',
7271
`
7372
function* foo() {
7473
let index = 0;
@@ -112,6 +111,18 @@ declare const foo: Error | string;
112111
throw foo as Error;
113112
`,
114113
'throw new Error() as Error;',
114+
`
115+
declare const nullishError: Error | undefined;
116+
throw nullishError ?? new Error();
117+
`,
118+
`
119+
declare const nullishError: Error | undefined;
120+
throw nullishError || new Error();
121+
`,
122+
`
123+
declare const nullishError: Error | undefined;
124+
throw nullishError ? nullishError : new Error();
125+
`,
115126
],
116127
invalid: [
117128
{
@@ -207,19 +218,31 @@ throw a + 'b';
207218
},
208219
{
209220
code: "throw 'literal' && 'not an Error';",
210-
errors: [
211-
{
212-
messageId: 'object',
213-
},
214-
],
221+
errors: [{ messageId: 'object' }],
222+
},
223+
{
224+
code: "throw 'literal' || new Error();",
225+
errors: [{ messageId: 'object' }],
226+
},
227+
{
228+
code: "throw new Error() && 'literal';",
229+
errors: [{ messageId: 'object' }],
230+
},
231+
{
232+
code: "throw 'literal' ?? new Error();",
233+
errors: [{ messageId: 'object' }],
215234
},
216235
{
217236
code: "throw foo ? 'not an Error' : 'literal';",
218-
errors: [
219-
{
220-
messageId: 'object',
221-
},
222-
],
237+
errors: [{ messageId: 'object' }],
238+
},
239+
{
240+
code: "throw foo ? new Error() : 'literal';",
241+
errors: [{ messageId: 'object' }],
242+
},
243+
{
244+
code: "throw foo ? 'literal' : new Error();",
245+
errors: [{ messageId: 'object' }],
223246
},
224247
{
225248
code: 'throw `${err}`;',

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy