Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(eslint-plugin): handle error classes using generics #1428

Merged
merged 2 commits into from Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/eslint-plugin/src/rules/no-throw-literal.ts
Expand Up @@ -29,7 +29,11 @@ export default util.createRule({

function isErrorLike(type: ts.Type): boolean {
const symbol = type.getSymbol();
if (symbol?.getName() === 'Error') {
if (!symbol) {
return false;
}

if (symbol.getName() === 'Error') {
const declarations = symbol.getDeclarations() ?? [];
for (const declaration of declarations) {
const sourceFile = declaration.getSourceFile();
Expand All @@ -39,7 +43,7 @@ export default util.createRule({
}
}

const baseTypes = type.getBaseTypes() ?? [];
const baseTypes = checker.getBaseTypes(type as ts.InterfaceType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting. Maybe we should add a lint rule against using type.getBaseTypes(), considering it fails in generic cases.

for (const baseType of baseTypes) {
if (isErrorLike(baseType)) {
return true;
Expand Down
49 changes: 37 additions & 12 deletions packages/eslint-plugin/tests/rules/no-throw-literal.test.ts
Expand Up @@ -14,8 +14,8 @@ const ruleTester = new RuleTester({
ruleTester.run('no-throw-literal', rule, {
valid: [
'throw new Error();',
"throw new Error('error');",
"throw Error('error');",
'throw new Error("error");',
'throw Error("error");',
`
const e = new Error();
throw e;
Expand Down Expand Up @@ -65,16 +65,28 @@ throw new CustomError();
`,
'throw foo = new Error();',
'throw 1, 2, new Error();',
"throw 'literal' && new Error();",
"throw new Error() || 'literal'",
"throw foo ? new Error() : 'literal';",
"throw foo ? 'literal' : new Error();",
'throw "literal" && new Error();',
'throw new Error() || "literal"',
'throw foo ? new Error() : "literal";',
'throw foo ? "literal" : new Error();',
'function* foo() { let index = 0; throw yield index++; }',
'async function foo() { throw await bar; }',
`
import { Error } from './missing';
throw Error;
`,
`
class CustomError<T, C> extends Error {}
throw new CustomError<string, string>();
`,
`
class CustomError<T = {}> extends Error {}
throw new CustomError();
`,
`
class CustomError<T extends object> extends Error {}
throw new CustomError();
`,
],
invalid: [
{
Expand All @@ -86,15 +98,15 @@ throw Error;
],
},
{
code: "throw new String('');",
code: 'throw new String("");',
errors: [
{
messageId: 'object',
},
],
},
{
code: "throw 'error';",
code: 'throw "error";',
errors: [
{
messageId: 'object',
Expand Down Expand Up @@ -134,7 +146,7 @@ throw Error;
],
},
{
code: "throw 'a' + 'b';",
code: 'throw "a" + "b";',
errors: [
{
messageId: 'object',
Expand All @@ -153,7 +165,7 @@ throw a + 'b';
],
},
{
code: "throw foo = 'error';",
code: 'throw foo = "error";',
errors: [
{
messageId: 'object',
Expand All @@ -169,15 +181,15 @@ throw a + 'b';
],
},
{
code: "throw 'literal' && 'not an Error';",
code: 'throw "literal" && "not an Error";',
errors: [
{
messageId: 'object',
},
],
},
{
code: "throw foo ? 'not an Error' : 'literal';",
code: 'throw foo ? "not an Error" : "literal";',
errors: [
{
messageId: 'object',
Expand Down Expand Up @@ -288,5 +300,18 @@ throw new Error();
},
],
},
{
code: `
class CustomError<T extends object> extends Foo {}
throw new CustomError();
`,
errors: [
{
messageId: 'object',
line: 3,
column: 7,
},
],
},
],
});