From 6f397df42cbcf05c10f304c9bbfdae4803aa0ce2 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 17 Aug 2020 16:38:46 -0700 Subject: [PATCH 01/12] chore: update issue templates (#2397) --- .../ISSUE_TEMPLATE/eslint-plugin-tslint.md | 24 ++++++++++-- .../eslint-plugin-typescript.md | 24 +++++++++--- .../typescript-eslint-parser.md | 29 +++++++++++++-- .../ISSUE_TEMPLATE/typescript-eslint-utils.md | 36 +++++++++++------- .github/ISSUE_TEMPLATE/typescript-estree.md | 37 ++++++++++++------- 5 files changed, 111 insertions(+), 39 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md b/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md index e999830e4f18..3931ecdf5a4f 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md @@ -14,15 +14,24 @@ The more relevant information you can include, the faster we can find the issue --> +- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have updated to the latest version of the packages. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. + **Repro** ```JSON @@ -50,8 +59,18 @@ Also include your tslint config, if you're using a separate file. **Expected Result** + + **Actual Result** + + **Additional Info** - +- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have updated to the latest version of the packages. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. **Repro** ```JSON @@ -50,10 +51,24 @@ The more irrelevant code/config you give, the harder it is for us to investigate // your repro code case ``` + + **Expected Result** + + **Actual Result** + + **Additional Info** +- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have updated to the latest version of the packages. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. + **Repro** ```JSON @@ -40,10 +50,24 @@ The more irrelevant code/config you give, the harder it is for us to investigate // your repro code case ``` + + **Expected Result** + + **Actual Result** + + **Additional Info** +- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have updated to the latest version of the packages. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. + **Repro** -```JSON -{ - "rules": { - "@typescript-eslint/": [""] - }, - "parserOptions": { - "...": "something" - } -} -``` +Please consider creating an isolated reproduction repo to make it easy for the volunteer maintainers debug your issue. +--> ```TS // your repro code case @@ -42,8 +41,18 @@ The more irrelevant code/config you give, the harder it is for us to investigate **Expected Result** + + **Actual Result** + + **Additional Info** +- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have updated to the latest version of the packages. +- [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. + **Repro** -```JSON -{ - "rules": { - "@typescript-eslint/": [""] - }, - "parserOptions": { - "...": "something" - } -} +```TS +// the code you're trying to parse ``` ```TS -// your repro code case +// the code you're using to do the parse of the aforementioned code ``` **Expected Result** + + **Actual Result** + + **Additional Info** -- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. - [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md index 153e14a8d2d7..aa4a0122a40a 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md @@ -26,7 +26,7 @@ Are you opening an issue because the rule you're trying to use is not found? 3) If ESLint still can't find the rule, then consider reporting an issue. --> -- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. - [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. diff --git a/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md b/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md index 541051bbcce7..5e038b4eb066 100644 --- a/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md +++ b/.github/ISSUE_TEMPLATE/typescript-eslint-parser.md @@ -22,7 +22,7 @@ That means you should only use this template if there are problems parsing some If you have a problem with a specific lint rule, please back out and select the `@typescript-eslint/eslint-plugin` template. --> -- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. - [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. diff --git a/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md index b90be4799c07..9445c2ff97fb 100644 --- a/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md +++ b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md @@ -22,7 +22,7 @@ If you have a problem with a specific lint rule, please back out and select the If you have a problem with the parser, please back out and select the `@typescript-eslint/parser` template. --> -- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. - [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. diff --git a/.github/ISSUE_TEMPLATE/typescript-estree.md b/.github/ISSUE_TEMPLATE/typescript-estree.md index 06b170f90526..f2bd8cae68ba 100644 --- a/.github/ISSUE_TEMPLATE/typescript-estree.md +++ b/.github/ISSUE_TEMPLATE/typescript-estree.md @@ -21,7 +21,7 @@ This issue template is only for problems specifically with the `@typescript-esli If you have a problem with a specific lint rule, please back out and select the `@typescript-eslint/eslint-plugin` template. --> -- [ ] I have first restarting my IDE and the issue persists. +- [ ] I have tried restarting my IDE and the issue persists. - [ ] I have updated to the latest version of the packages. - [ ] I have [read the FAQ](https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md) and my problem is not listed. From 44e4d9b1af3a7cf3bed0009686737c7f8a279596 Mon Sep 17 00:00:00 2001 From: Alec Custer Date: Wed, 19 Aug 2020 13:29:26 -0400 Subject: [PATCH 03/12] docs: fix incorrect link to prettier package (#2404) --- docs/getting-started/linting/FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/linting/FAQ.md b/docs/getting-started/linting/FAQ.md index cfc50cb8501d..c6509622e83c 100644 --- a/docs/getting-started/linting/FAQ.md +++ b/docs/getting-started/linting/FAQ.md @@ -66,7 +66,7 @@ The following rules do not have equivalent checks in TypeScript, so we recommend This rule helps ensure your codebase follows a consistent indentation pattern. However this involves a _lot_ of computations across every single token in a file. Across a large codebase, these can add up, and severely impact performance. -We recommend not using this rule, and instead using a tool like [`prettier`](https://www.npmjs.com/package/) to enforce a standardized formatting. +We recommend not using this rule, and instead using a tool like [`prettier`](https://www.npmjs.com/package/prettier) to enforce a standardized formatting.

From b62331ad02dcff3236e18f10eb92d59e7371d4c3 Mon Sep 17 00:00:00 2001 From: Chris Brody Date: Thu, 20 Aug 2020 13:00:32 -0400 Subject: [PATCH 04/12] fix(typescript-estree): ts.NamedTupleMember workaround for ; export type TSNode = From 3764248084455409f085c5bc4706079405cef618 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Thu, 20 Aug 2020 22:24:57 +0300 Subject: [PATCH 05/12] fix(eslint-plugin): [explicit-module-boundary-types] ignore abstract setters (#2410) --- .../src/rules/explicit-module-boundary-types.ts | 5 ++++- .../tests/rules/explicit-module-boundary-types.test.ts | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts index 49fdfc1d5a08..fc2dd4e63c40 100644 --- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts +++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts @@ -427,7 +427,10 @@ export default util.createRule({ const isConstructor = node.parent?.type === AST_NODE_TYPES.MethodDefinition && node.parent.kind === 'constructor'; - if (!isConstructor && !node.returnType) { + const isAbstractSetAccessor = + node.parent?.type === AST_NODE_TYPES.TSAbstractMethodDefinition && + node.parent.kind === 'set'; + if (!isConstructor && !isAbstractSetAccessor && !node.returnType) { context.report({ node, messageId: 'missingReturnType', diff --git a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts index 114b26aed0f1..32ae93592b42 100644 --- a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts @@ -646,6 +646,11 @@ type Buz = () => (n: number) => string; export const buz: Buz = () => n => String(n); `, + ` +export abstract class Foo { + abstract set value(element: T); +} + `, ], invalid: [ { From fde89d4d392ef35cac2bc09f2774bfe397b20100 Mon Sep 17 00:00:00 2001 From: phiresky Date: Fri, 21 Aug 2020 10:30:11 +0200 Subject: [PATCH 06/12] feat(eslint-plugin): add `no-implicit-any-catch` rule (#2202) Co-authored-by: Glen Co-authored-by: Brad Zacher --- packages/eslint-plugin/README.md | 1 + .../docs/rules/no-implicit-any-catch.md | 76 +++++++++++++++ packages/eslint-plugin/src/configs/all.ts | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/no-implicit-any-catch.ts | 95 +++++++++++++++++++ .../tests/rules/no-implicit-any-catch.test.ts | 78 +++++++++++++++ 6 files changed, 253 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/no-implicit-any-catch.md create mode 100644 packages/eslint-plugin/src/rules/no-implicit-any-catch.ts create mode 100644 packages/eslint-plugin/tests/rules/no-implicit-any-catch.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index cb8a5546142c..031db6937944 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -122,6 +122,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-extraneous-class`](./docs/rules/no-extraneous-class.md) | Forbids the use of classes as namespaces | | | | | [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Requires Promise-like values to be handled appropriately | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-for-in-array`](./docs/rules/no-for-in-array.md) | Disallow iterating over an array with a for-in loop | :heavy_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/no-implicit-any-catch`](./docs/rules/no-implicit-any-catch.md) | Disallow usage of the implicit `any` type in catch clauses | | :wrench: | | | [`@typescript-eslint/no-implied-eval`](./docs/rules/no-implied-eval.md) | Disallow the use of `eval()`-like methods | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-inferrable-types`](./docs/rules/no-inferrable-types.md) | Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/no-invalid-void-type`](./docs/rules/no-invalid-void-type.md) | Disallows usage of `void` type outside of generic or return types | | | | diff --git a/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md b/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md new file mode 100644 index 000000000000..8ef946b42865 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-implicit-any-catch.md @@ -0,0 +1,76 @@ +# Disallow usage of the implicit `any` type in catch clauses (`no-implicit-any-catch`) + +TypeScript 4.0 added support for adding an explicit `any` or `unknown` type annotation on a catch clause variable. + +By default, TypeScript will type a catch clause variable as `any`, so explicitly annotating it as `unknown` can add a lot of safety to your codebase. + +The `noImplicitAny` flag in TypeScript does not cover this for backwards compatibility reasons. + +## Rule Details + +This rule requires an explicit type to be declared on a catch clause variable. + +The following pattern is considered a warning: + +```ts +try { + // ... +} catch (e) { + // ... +} +``` + +The following pattern is **_not_** considered a warning: + + + + +```ts +try { + // ... +} catch (e: unknown) { + // ... +} +``` + + + +## Options + +The rule accepts an options object with the following properties: + +```ts +type Options = { + // if false, disallow specifying `: any` as the error type as well. See also `no-explicit-any` + allowExplicitAny: boolean; +}; + +const defaults = { + allowExplicitAny: false, +}; +``` + +### `allowExplicitAny` + +The follow is is **_not_** considered a warning with `{ allowExplicitAny: true }` + + + + +```ts +try { + // ... +} catch (e: any) { + // ... +} +``` + + + +## When Not To Use It + +If you are not using TypeScript 4.0 (or greater), then you will not be able to use this rule, annotations on catch clauses is not supported. + +## Further Reading + +- [TypeScript 4.0 Beta Release Notes](https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/#unknown-on-catch) diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 8361bde29b2c..c0105c354c2d 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -58,6 +58,7 @@ export = { '@typescript-eslint/no-extraneous-class': 'error', '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-for-in-array': 'error', + '@typescript-eslint/no-implicit-any-catch': 'error', '@typescript-eslint/no-implied-eval': 'error', '@typescript-eslint/no-inferrable-types': 'error', 'no-invalid-this': 'off', diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 85d0642fc791..63acccee914d 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -31,6 +31,7 @@ import noDynamicDelete from './no-dynamic-delete'; import noEmptyFunction from './no-empty-function'; import noEmptyInterface from './no-empty-interface'; import noExplicitAny from './no-explicit-any'; +import noImplicitAnyCatch from './no-implicit-any-catch'; import noExtraneousClass from './no-extraneous-class'; import noExtraNonNullAssertion from './no-extra-non-null-assertion'; import noExtraParens from './no-extra-parens'; @@ -133,6 +134,7 @@ export default { 'no-empty-function': noEmptyFunction, 'no-empty-interface': noEmptyInterface, 'no-explicit-any': noExplicitAny, + 'no-implicit-any-catch': noImplicitAnyCatch, 'no-extra-non-null-assertion': noExtraNonNullAssertion, 'no-extra-parens': noExtraParens, 'no-extra-semi': noExtraSemi, diff --git a/packages/eslint-plugin/src/rules/no-implicit-any-catch.ts b/packages/eslint-plugin/src/rules/no-implicit-any-catch.ts new file mode 100644 index 000000000000..aed526f8ab01 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-implicit-any-catch.ts @@ -0,0 +1,95 @@ +import * as util from '../util'; +import { + TSESLint, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; + +export type Options = [ + { + allowExplicitAny: boolean; + }, +]; +export type MessageIds = + | 'implicitAnyInCatch' + | 'explicitAnyInCatch' + | 'suggestExplicitUnknown'; + +export default util.createRule({ + name: 'no-implicit-any-catch', + meta: { + type: 'suggestion', + docs: { + description: 'Disallow usage of the implicit `any` type in catch clauses', + category: 'Best Practices', + recommended: false, + suggestion: true, + }, + fixable: 'code', + messages: { + implicitAnyInCatch: 'Implicit any in catch clause', + explicitAnyInCatch: 'Explicit any in catch clause', + suggestExplicitUnknown: + 'Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.', + }, + schema: [ + { + type: 'object', + additionalProperties: false, + properties: { + allowExplicitAny: { + type: 'boolean', + }, + }, + }, + ], + }, + defaultOptions: [ + { + allowExplicitAny: false, + }, + ], + create(context, [{ allowExplicitAny }]) { + return { + CatchClause(node): void { + if (!node.param) { + return; // ignore catch without variable + } + + if (!node.param.typeAnnotation) { + context.report({ + node, + messageId: 'implicitAnyInCatch', + suggest: [ + { + messageId: 'suggestExplicitUnknown', + fix(fixer): TSESLint.RuleFix { + return fixer.insertTextAfter(node.param!, ': unknown'); + }, + }, + ], + }); + } else if ( + !allowExplicitAny && + node.param.typeAnnotation.typeAnnotation.type === + AST_NODE_TYPES.TSAnyKeyword + ) { + context.report({ + node, + messageId: 'explicitAnyInCatch', + suggest: [ + { + messageId: 'suggestExplicitUnknown', + fix(fixer): TSESLint.RuleFix { + return fixer.replaceText( + node.param!.typeAnnotation!, + ': unknown', + ); + }, + }, + ], + }); + } + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-implicit-any-catch.test.ts b/packages/eslint-plugin/tests/rules/no-implicit-any-catch.test.ts new file mode 100644 index 000000000000..f8113555086e --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-implicit-any-catch.test.ts @@ -0,0 +1,78 @@ +/* eslint-disable eslint-comments/no-use */ +// TODO - prettier currently removes the type annotations, re-enable this once prettier is updated +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + +import rule from '../../src/rules/no-implicit-any-catch'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-implicit-any-catch', rule, { + valid: [ + ` +try { +} catch (e1: unknown) {} + `, + { + code: ` +try { +} catch (e2: any) {} + `, + options: [{ allowExplicitAny: true }], + }, + ], + invalid: [ + { + code: ` +try { +} catch (e3) {} + `.trim(), + errors: [ + { + line: 2, + column: 3, + messageId: 'implicitAnyInCatch', + endLine: 2, + endColumn: 16, + suggestions: [ + { + messageId: 'suggestExplicitUnknown', + output: ` +try { +} catch (e3: unknown) {} + `.trim(), + }, + ], + }, + ], + }, + { + code: ` +try { +} catch (e4: any) {} + `.trim(), + options: [{ allowExplicitAny: false }], + errors: [ + { + line: 2, + column: 3, + messageId: 'explicitAnyInCatch', + endLine: 2, + endColumn: 21, + suggestions: [ + { + messageId: 'suggestExplicitUnknown', + output: ` +try { +} catch (e4: unknown) {} + `.trim(), + }, + ], + }, + ], + }, + ], +}); From fd2070c4c669885f003b8887890aedac0c03139f Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Fri, 21 Aug 2020 01:43:21 -0700 Subject: [PATCH 07/12] docs(eslint-plugin): [no-non-null-asserted-optional-chain] correct docs for 3.9 functionality Closes #2412 --- .../rules/no-non-null-asserted-optional-chain.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md b/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md index 95ee927b22e2..d8bd7dcfa11d 100644 --- a/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md +++ b/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md @@ -5,14 +5,20 @@ Optional chain expressions are designed to return `undefined` if the optional property is nullish. Using non-null assertions after an optional chain expression is wrong, and introduces a serious type safety hole into your code. + + Examples of **incorrect** code for this rule: ```ts /* eslint @typescript-eslint/no-non-null-asserted-optional-chain: "error" */ foo?.bar!; -foo?.bar!.baz; foo?.bar()!; + +// Prior to TS3.9, foo?.bar!.baz meant (foo?.bar).baz - i.e. the non-null assertion is applied to the entire chain so far. +// For TS3.9 and greater, the non-null assertion is only applied to the property itself, so it's safe. +// The following is incorrect code if you're using less than TS3.9 +foo?.bar!.baz; foo?.bar!(); foo?.bar!().baz; ``` @@ -27,6 +33,11 @@ foo?.bar; foo?.bar(); foo?.bar(); foo?.bar().baz; + +// The following is correct code if you're using TS3.9 or greater +foo?.bar!.baz; +foo?.bar!(); +foo?.bar!().baz; ``` ## When Not To Use It From d468cfe10e49e96d59f2ea4bc50cc0db0f03541f Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Fri, 21 Aug 2020 02:03:50 -0700 Subject: [PATCH 08/12] docs(eslint-plugin): [no-non-null-asserted-optional-chain] fix formatting --- .../docs/rules/no-non-null-asserted-optional-chain.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md b/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md index d8bd7dcfa11d..fdb2a8a18f08 100644 --- a/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md +++ b/packages/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md @@ -5,8 +5,6 @@ Optional chain expressions are designed to return `undefined` if the optional property is nullish. Using non-null assertions after an optional chain expression is wrong, and introduces a serious type safety hole into your code. - - Examples of **incorrect** code for this rule: ```ts From a53f8c6ff37aa47b3fc1b729e359d81ea079ff75 Mon Sep 17 00:00:00 2001 From: Sergey Bakulin Date: Fri, 21 Aug 2020 23:15:59 +0300 Subject: [PATCH 09/12] fix(eslint-plugin): [explicit-module-boundary-types] ignore all bodyless setters (#2413) --- .../src/rules/explicit-module-boundary-types.ts | 7 ++++--- .../tests/rules/explicit-module-boundary-types.test.ts | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts index fc2dd4e63c40..5671e36dbdd2 100644 --- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts +++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts @@ -427,10 +427,11 @@ export default util.createRule({ const isConstructor = node.parent?.type === AST_NODE_TYPES.MethodDefinition && node.parent.kind === 'constructor'; - const isAbstractSetAccessor = - node.parent?.type === AST_NODE_TYPES.TSAbstractMethodDefinition && + const isSetAccessor = + (node.parent?.type === AST_NODE_TYPES.TSAbstractMethodDefinition || + node.parent?.type === AST_NODE_TYPES.MethodDefinition) && node.parent.kind === 'set'; - if (!isConstructor && !isAbstractSetAccessor && !node.returnType) { + if (!isConstructor && !isSetAccessor && !node.returnType) { context.report({ node, messageId: 'missingReturnType', diff --git a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts index 32ae93592b42..c79cc34a314a 100644 --- a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts @@ -649,6 +649,11 @@ export const buz: Buz = () => n => String(n); ` export abstract class Foo { abstract set value(element: T); +} + `, + ` +export declare class Foo { + set time(seconds: number); } `, ], From e6be62128b3a98541fe590512892c4b501914e46 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 23 Aug 2020 17:20:04 -0700 Subject: [PATCH 10/12] feat(typescript-estree): update allowed TS version range (#2419) TS v4.0 is released, we can now bump our range to allow it, as we have full support for it. --- README.md | 2 +- package.json | 4 ++-- packages/typescript-estree/src/parser.ts | 2 +- yarn.lock | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f1f6f77c6460..9f96d4bf0557 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ The latest version under the `canary` tag **(latest commit to master)** is: ## Supported TypeScript Version -**The version range of TypeScript currently supported by this parser is `>=3.3.1 <3.10.0`.** +**The version range of TypeScript currently supported by this parser is `>=3.3.1 <4.1.0`.** These versions are what we test against. diff --git a/package.json b/package.json index 97be489b4c12..c130fec5ce10 100644 --- a/package.json +++ b/package.json @@ -98,9 +98,9 @@ "ts-jest": "^25.5.1", "ts-node": "^8.10.1", "tslint": "^6.1.2", - "typescript": ">=3.3.1 <4.0.0 || 4.0.0-beta" + "typescript": ">=3.3.1 <4.1.0" }, "resolutions": { - "typescript": "4.0.0-beta" + "typescript": "4.0.2" } } diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 88fa5d503b8f..d307eac42bf2 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -20,7 +20,7 @@ const log = debug('typescript-eslint:typescript-estree:parser'); * This needs to be kept in sync with the top-level README.md in the * typescript-eslint monorepo */ -const SUPPORTED_TYPESCRIPT_VERSIONS = '>=3.3.1 <3.10.0'; +const SUPPORTED_TYPESCRIPT_VERSIONS = '>=3.3.1 <4.1.0'; /* * The semver package will ignore prerelease ranges, and we don't want to explicitly document every one * List them all separately here, so we can automatically create the full string diff --git a/yarn.lock b/yarn.lock index 2f7721577ec9..dcf997873689 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8610,10 +8610,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@*, typescript@4.0.0-beta, "typescript@>=3.3.1 <4.0.0 || 4.0.0-beta", typescript@^3.8.0-dev.20200111: - version "4.0.0-beta" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-beta.tgz#a6a65e430562131de69496a3ef5484346bc0cdd2" - integrity sha512-d3s/CogGtB2uPZ2Z8ts6eoUxxyB9PH3R27/UrzvpthuOvpCg4FWWnBbBiqJ0K4eu6eTlgmLiqQkh2dquReJweA== +typescript@*, typescript@4.0.2, "typescript@>=3.3.1 <4.1.0", typescript@^3.8.0-dev.20200111: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" From 32fe2bb4fe5524355eef4f3a9bd85c824e9d7f46 Mon Sep 17 00:00:00 2001 From: Saraswathy Renuga Date: Mon, 24 Aug 2020 05:55:07 +0530 Subject: [PATCH 11/12] fix(eslint-plugin): [no-unnecessary-condition] better handling for unary negation (#2382) --- .../src/rules/no-unnecessary-condition.ts | 32 ++++++++++++ .../rules/no-unnecessary-condition.test.ts | 49 ++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 22fec531779b..29f9aa4442d2 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -166,6 +166,14 @@ export default createRule({ * if the type of the node is always true or always false, it's not necessary. */ function checkNode(node: TSESTree.Expression): void { + // Check if the node is Unary Negation expression and handle it + if ( + node.type === AST_NODE_TYPES.UnaryExpression && + node.operator === '!' + ) { + return checkIfUnaryNegationExpressionIsNecessaryConditional(node); + } + // Since typescript array index signature types don't represent the // possibility of out-of-bounds access, if we're indexing into an array // just skip the check, to avoid false positives @@ -213,6 +221,30 @@ export default createRule({ } } + /** + * If it is Unary Negation Expression, check the argument for alwaysTruthy / alwaysFalsy + */ + function checkIfUnaryNegationExpressionIsNecessaryConditional( + node: TSESTree.UnaryExpression, + ): void { + if (isArrayIndexExpression(node.argument)) { + return; + } + const type = getNodeType(node.argument); + const messageId = isTypeFlagSet(type, ts.TypeFlags.Never) + ? 'never' + : isPossiblyTruthy(type) + ? 'alwaysFalsy' + : isPossiblyFalsy(type) + ? 'alwaysTruthy' + : undefined; + + if (messageId) { + context.report({ node, messageId }); + } + return; + } + function checkNodeForNullish(node: TSESTree.Expression): void { // Since typescript array index signature types don't represent the // possibility of out-of-bounds access, if we're indexing into an array diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index 55b7a0e7fb1c..3a9d0fb450bc 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -445,7 +445,26 @@ declare const key: Key; foo?.[key]?.trim(); `, - // https://github.com/typescript-eslint/typescript-eslint/issues/2384 + ` +let latencies: number[][] = []; + +function recordData(): void { + if (!latencies[0]) latencies[0] = []; + latencies[0].push(4); +} + +recordData(); + `, + ` +let latencies: number[][] = []; + +function recordData(): void { + if (latencies[0]) latencies[0] = []; + latencies[0].push(4); +} + +recordData(); + `, ` function test(testVal?: boolean) { if (testVal ?? true) { @@ -458,6 +477,34 @@ function test(testVal?: boolean) { // Ensure that it's checking in all the right places { code: ` +const a = null; +if (!a) { +} + `, + errors: [ruleError(3, 5, 'alwaysTruthy')], + }, + { + code: ` +const a = true; +if (!a) { +} + `, + errors: [ruleError(3, 5, 'alwaysFalsy')], + }, + { + code: ` +function sayHi(): void { + console.log('Hi!'); +} + +let speech: never = sayHi(); +if (!speech) { +} + `, + errors: [ruleError(7, 5, 'never')], + }, + { + code: ` const b1 = true; declare const b2: boolean; const t1 = b1 && b2; From 50f9c4afcb08f65a5c6410e8a08f90df6f266cac Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 24 Aug 2020 17:02:28 +0000 Subject: [PATCH 12/12] chore: publish v3.10.0 --- CHANGELOG.md | 20 ++++++++++++++++++++ lerna.json | 2 +- packages/eslint-plugin-internal/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-internal/package.json | 4 ++-- packages/eslint-plugin-tslint/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-tslint/package.json | 6 +++--- packages/eslint-plugin/CHANGELOG.md | 18 ++++++++++++++++++ packages/eslint-plugin/package.json | 4 ++-- packages/experimental-utils/CHANGELOG.md | 8 ++++++++ packages/experimental-utils/package.json | 6 +++--- packages/parser/CHANGELOG.md | 8 ++++++++ packages/parser/package.json | 10 +++++----- packages/scope-manager/CHANGELOG.md | 8 ++++++++ packages/scope-manager/package.json | 8 ++++---- packages/shared-fixtures/CHANGELOG.md | 8 ++++++++ packages/shared-fixtures/package.json | 2 +- packages/types/CHANGELOG.md | 8 ++++++++ packages/types/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 16 ++++++++++++++++ packages/typescript-estree/package.json | 8 ++++---- packages/visitor-keys/CHANGELOG.md | 8 ++++++++ packages/visitor-keys/package.json | 4 ++-- 22 files changed, 146 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68bf63b27e67..84849c434bba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.10.0](https://github.com/typescript-eslint/typescript-eslint/compare/v3.9.1...v3.10.0) (2020-08-24) + + +### Bug Fixes + +* **eslint-plugin:** [explicit-module-boundary-types] ignore abstract setters ([#2410](https://github.com/typescript-eslint/typescript-eslint/issues/2410)) ([3764248](https://github.com/typescript-eslint/typescript-eslint/commit/3764248084455409f085c5bc4706079405cef618)) +* **eslint-plugin:** [explicit-module-boundary-types] ignore all bodyless setters ([#2413](https://github.com/typescript-eslint/typescript-eslint/issues/2413)) ([a53f8c6](https://github.com/typescript-eslint/typescript-eslint/commit/a53f8c6ff37aa47b3fc1b729e359d81ea079ff75)) +* **eslint-plugin:** [no-unnecessary-condition] better handling for unary negation ([#2382](https://github.com/typescript-eslint/typescript-eslint/issues/2382)) ([32fe2bb](https://github.com/typescript-eslint/typescript-eslint/commit/32fe2bb4fe5524355eef4f3a9bd85c824e9d7f46)) +* **typescript-estree:** ts.NamedTupleMember workaround for