From 017b22d2e9fafd8322b3c1d7139f6714ddab9e58 Mon Sep 17 00:00:00 2001 From: Jens Duttke Date: Tue, 21 Apr 2020 18:28:19 +0200 Subject: [PATCH 1/9] docs(eslint-plugin): [explicit-function-return-type] fix typo (#1923) --- .../eslint-plugin/docs/rules/explicit-function-return-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md index aa94a29a0ef3..a0e4ebce5ea9 100644 --- a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md +++ b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md @@ -70,7 +70,7 @@ type Options = { // if true, functions immediately returning another function expression will not be checked allowHigherOrderFunctions?: boolean; // if true, concise arrow functions that start with the void keyword will not be checked - allowConciseArrowFunctionExpressionStartingWithVoid?: boolean; + allowConciseArrowFunctionExpressionsStartingWithVoid?: boolean; }; const defaults = { From 4ef67884962b6aac61cc895aaa3ba16aa892ecf4 Mon Sep 17 00:00:00 2001 From: ulrichb Date: Tue, 21 Apr 2020 18:30:43 +0200 Subject: [PATCH 2/9] feat(experimental-utils): allow rule options to be a readonly tuple (#1924) --- .../experimental-utils/src/eslint-utils/RuleCreator.ts | 2 +- .../experimental-utils/src/eslint-utils/applyDefault.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts index f9278dc81549..4dede52bd7c6 100644 --- a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts +++ b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts @@ -17,7 +17,7 @@ function RuleCreator(urlCreator: (ruleName: string) => string) { // This function will get much easier to call when this is merged https://github.com/Microsoft/TypeScript/pull/26349 // TODO - when the above PR lands; add type checking for the context.report `data` property return function createRule< - TOptions extends unknown[], + TOptions extends readonly unknown[], TMessageIds extends string, TRuleListener extends RuleListener = RuleListener >({ diff --git a/packages/experimental-utils/src/eslint-utils/applyDefault.ts b/packages/experimental-utils/src/eslint-utils/applyDefault.ts index 142c8d1a7469..9b4f6513ecc6 100644 --- a/packages/experimental-utils/src/eslint-utils/applyDefault.ts +++ b/packages/experimental-utils/src/eslint-utils/applyDefault.ts @@ -7,12 +7,14 @@ import { deepMerge, isObjectNotArray } from './deepMerge'; * @param userOptions the user opts * @returns the options with defaults */ -function applyDefault( +function applyDefault( defaultOptions: TDefault, userOptions: TUser | null, ): TDefault { // clone defaults - const options: TDefault = JSON.parse(JSON.stringify(defaultOptions)); + const options: AsMutable = JSON.parse( + JSON.stringify(defaultOptions), + ); if (userOptions === null || userOptions === undefined) { return options; @@ -33,4 +35,6 @@ function applyDefault( return options; } +type AsMutable = { -readonly [TKey in keyof T]: T[TKey] }; + export { applyDefault }; From 93fb661d803467d807d5bf1592dbc9ddfc5d4529 Mon Sep 17 00:00:00 2001 From: AZMCode <15837568+AZMCode@users.noreply.github.com> Date: Fri, 24 Apr 2020 11:59:06 -0400 Subject: [PATCH 3/9] chore(eslint-plugin): correct grammatical mistake (#1932) --- packages/eslint-plugin/docs/rules/no-extraneous-class.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/no-extraneous-class.md b/packages/eslint-plugin/docs/rules/no-extraneous-class.md index b387d67a9933..abc84f272b4d 100644 --- a/packages/eslint-plugin/docs/rules/no-extraneous-class.md +++ b/packages/eslint-plugin/docs/rules/no-extraneous-class.md @@ -58,7 +58,7 @@ type Options = { allowEmpty?: boolean; // allow extraneous classes if they only contain static members allowStaticOnly?: boolean; - // allow extraneous classes if they are have a decorator + // allow extraneous classes if they have a decorator allowWithDecorator?: boolean; }; From f91ff20ca590c95a25bdc9de185aab54746f9ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Strnad?= <43024885+vostrnad@users.noreply.github.com> Date: Sun, 26 Apr 2020 20:36:06 +0200 Subject: [PATCH 4/9] chore(eslint-plugin): standardise sentences in rule messages (#1935) --- .../src/rules/consistent-type-definitions.ts | 4 ++-- .../eslint-plugin/src/rules/naming-convention.ts | 2 +- .../eslint-plugin/src/rules/no-floating-promises.ts | 6 +++--- .../rules/no-unnecessary-boolean-literal-compare.ts | 2 +- .../src/rules/no-unnecessary-condition.ts | 12 ++++++------ .../eslint-plugin/src/rules/no-unsafe-assignment.ts | 10 +++++----- packages/eslint-plugin/src/rules/no-unsafe-call.ts | 6 +++--- .../src/rules/no-unsafe-member-access.ts | 4 ++-- packages/eslint-plugin/src/rules/no-unsafe-return.ts | 2 +- .../src/rules/no-untyped-public-signature.ts | 4 ++-- .../src/rules/no-unused-vars-experimental.ts | 2 +- packages/eslint-plugin/src/rules/prefer-as-const.ts | 4 ++-- packages/eslint-plugin/src/rules/prefer-for-of.ts | 2 +- .../src/rules/prefer-readonly-parameter-types.ts | 2 +- packages/eslint-plugin/src/rules/return-await.ts | 6 +++--- .../src/rules/switch-exhaustiveness-check.ts | 2 +- packages/eslint-plugin/src/rules/typedef.ts | 4 ++-- 17 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-type-definitions.ts b/packages/eslint-plugin/src/rules/consistent-type-definitions.ts index d8d574d4d253..794e380e389d 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-definitions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-definitions.ts @@ -17,8 +17,8 @@ export default util.createRule({ recommended: false, }, messages: { - interfaceOverType: 'Use an `interface` instead of a `type`', - typeOverInterface: 'Use a `type` instead of an `interface`', + interfaceOverType: 'Use an `interface` instead of a `type`.', + typeOverInterface: 'Use a `type` instead of an `interface`.', }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/naming-convention.ts b/packages/eslint-plugin/src/rules/naming-convention.ts index f1963b17321b..69791b205c56 100644 --- a/packages/eslint-plugin/src/rules/naming-convention.ts +++ b/packages/eslint-plugin/src/rules/naming-convention.ts @@ -349,7 +349,7 @@ export default util.createRule({ unexpectedUnderscore: '{{type}} name {{name}} must not have a {{position}} underscore.', missingUnderscore: - '{{type}} name {{name}} must have a {{position}} underscore', + '{{type}} name {{name}} must have a {{position}} underscore.', missingAffix: '{{type}} name {{name}} must have one of the following {{position}}es: {{affixes}}', satisfyCustom: diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 09d70bac42c0..04da1a9b8c49 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -27,11 +27,11 @@ export default util.createRule({ requiresTypeChecking: true, }, messages: { - floating: 'Promises must be handled appropriately', + floating: 'Promises must be handled appropriately.', floatingVoid: 'Promises must be handled appropriately' + - ' or explicitly marked as ignored with the `void` operator', - floatingFixVoid: 'Add void operator to ignore', + ' or explicitly marked as ignored with the `void` operator.', + floatingFixVoid: 'Add void operator to ignore.', }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts b/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts index f0ebc0292ce1..1cd03962ae47 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts @@ -28,7 +28,7 @@ export default util.createRule<[], MessageIds>({ fixable: 'code', messages: { direct: - 'This expression unnecessarily compares a boolean value to a boolean instead of using it directly', + 'This expression unnecessarily compares a boolean value to a boolean instead of using it directly.', negated: 'This expression unnecessarily compares a boolean value to a boolean instead of negating it.', }, diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 07bb3d56c5c3..a64a3ddc9c94 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -108,17 +108,17 @@ export default createRule({ alwaysTruthy: 'Unnecessary conditional, value is always truthy.', alwaysFalsy: 'Unnecessary conditional, value is always falsy.', alwaysTruthyFunc: - 'This callback should return a conditional, but return is always truthy', + 'This callback should return a conditional, but return is always truthy.', alwaysFalsyFunc: - 'This callback should return a conditional, but return is always falsy', + 'This callback should return a conditional, but return is always falsy.', neverNullish: 'Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined.', alwaysNullish: - 'Unnecessary conditional, left-hand side of `??` operator is always `null` or `undefined`', + 'Unnecessary conditional, left-hand side of `??` operator is always `null` or `undefined`.', literalBooleanExpression: - 'Unnecessary conditional, both sides of the expression are literal values', - never: 'Unnecessary conditional, value is `never`', - neverOptionalChain: 'Unnecessary optional chain on a non-nullish value', + 'Unnecessary conditional, both sides of the expression are literal values.', + never: 'Unnecessary conditional, value is `never`.', + neverOptionalChain: 'Unnecessary optional chain on a non-nullish value.', }, }, defaultOptions: [ diff --git a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts index 60f7b78acc08..b7f339e19440 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts @@ -25,13 +25,13 @@ export default util.createRule({ requiresTypeChecking: true, }, messages: { - anyAssignment: 'Unsafe assignment of an any value', - unsafeArrayPattern: 'Unsafe array destructuring of an any array value', + anyAssignment: 'Unsafe assignment of an any value.', + unsafeArrayPattern: 'Unsafe array destructuring of an any array value.', unsafeArrayPatternFromTuple: - 'Unsafe array destructuring of a tuple element with an any value', + 'Unsafe array destructuring of a tuple element with an any value.', unsafeAssignment: - 'Unsafe asignment of type {{sender}} to a variable of type {{receiver}}', - unsafeArraySpread: 'Unsafe spread of an any value in an array', + 'Unsafe asignment of type {{sender}} to a variable of type {{receiver}}.', + unsafeArraySpread: 'Unsafe spread of an any value in an array.', }, schema: [], }, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 3661bd44f16d..4cd123708229 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -14,9 +14,9 @@ export default util.createRule<[], MessageIds>({ requiresTypeChecking: true, }, messages: { - unsafeCall: 'Unsafe call of an any typed value', - unsafeNew: 'Unsafe construction of an any type value', - unsafeTemplateTag: 'Unsafe any typed template tag', + unsafeCall: 'Unsafe call of an any typed value.', + unsafeNew: 'Unsafe construction of an any type value.', + unsafeTemplateTag: 'Unsafe any typed template tag.', }, schema: [], }, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts index 3d3b0c7e7231..a17818efb414 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts @@ -21,9 +21,9 @@ export default util.createRule({ }, messages: { unsafeMemberExpression: - 'Unsafe member access {{property}} on an any value', + 'Unsafe member access {{property}} on an any value.', unsafeComputedMemberAccess: - 'Computed name {{property}} resolves to an any value', + 'Computed name {{property}} resolves to an any value.', }, schema: [], }, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-return.ts b/packages/eslint-plugin/src/rules/no-unsafe-return.ts index 5fbbfac9b908..53429898da4e 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-return.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-return.ts @@ -18,7 +18,7 @@ export default util.createRule({ messages: { unsafeReturn: 'Unsafe return of an {{type}} typed value', unsafeReturnAssignment: - 'Unsafe return of type {{sender}} from function with return type {{receiver}}', + 'Unsafe return of type {{sender}} from function with return type {{receiver}}.', }, schema: [], }, diff --git a/packages/eslint-plugin/src/rules/no-untyped-public-signature.ts b/packages/eslint-plugin/src/rules/no-untyped-public-signature.ts index 303c1336b637..cff725facc9c 100644 --- a/packages/eslint-plugin/src/rules/no-untyped-public-signature.ts +++ b/packages/eslint-plugin/src/rules/no-untyped-public-signature.ts @@ -19,8 +19,8 @@ export default util.createRule({ recommended: false, }, messages: { - noReturnType: 'Public method has no return type', - untypedParameter: 'Public method parameters should be typed', + noReturnType: 'Public method has no return type.', + untypedParameter: 'Public method parameters should be typed.', }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/no-unused-vars-experimental.ts b/packages/eslint-plugin/src/rules/no-unused-vars-experimental.ts index 920a23199251..ca46ef0527c3 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars-experimental.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars-experimental.ts @@ -56,7 +56,7 @@ export default util.createRule({ messages: { unused: "{{type}} '{{name}}' is declared but its value is never read.", unusedWithIgnorePattern: - "{{type}} '{{name}}' is declared but its value is never read. Allowed unused names must match {{pattern}}", + "{{type}} '{{name}}' is declared but its value is never read. Allowed unused names must match {{pattern}}.", unusedImport: 'All imports in import declaration are unused.', unusedTypeParameters: 'All type parameters are unused.', }, diff --git a/packages/eslint-plugin/src/rules/prefer-as-const.ts b/packages/eslint-plugin/src/rules/prefer-as-const.ts index 1cf21af7580c..cbce99462d19 100644 --- a/packages/eslint-plugin/src/rules/prefer-as-const.ts +++ b/packages/eslint-plugin/src/rules/prefer-as-const.ts @@ -17,9 +17,9 @@ export default util.createRule({ fixable: 'code', messages: { preferConstAssertion: - 'Expected a `const` instead of a literal type assertion', + 'Expected a `const` instead of a literal type assertion.', variableConstAssertion: - 'Expected a `const` assertion instead of a literal type annotation', + 'Expected a `const` assertion instead of a literal type annotation.', variableSuggest: 'You should use `as const` instead of type annotation.', }, schema: [], diff --git a/packages/eslint-plugin/src/rules/prefer-for-of.ts b/packages/eslint-plugin/src/rules/prefer-for-of.ts index 40fac431664d..49fbc236f5d4 100644 --- a/packages/eslint-plugin/src/rules/prefer-for-of.ts +++ b/packages/eslint-plugin/src/rules/prefer-for-of.ts @@ -17,7 +17,7 @@ export default util.createRule({ }, messages: { preferForOf: - 'Expected a `for-of` loop instead of a `for` loop with this simple iteration', + 'Expected a `for-of` loop instead of a `for` loop with this simple iteration.', }, schema: [], }, diff --git a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts index 07e8e69940cc..fdad0220c39b 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts @@ -34,7 +34,7 @@ export default util.createRule({ }, ], messages: { - shouldBeReadonly: 'Parameter should be a read only type', + shouldBeReadonly: 'Parameter should be a read only type.', }, }, defaultOptions: [ diff --git a/packages/eslint-plugin/src/rules/return-await.ts b/packages/eslint-plugin/src/rules/return-await.ts index d18e41ef00e0..173596e451fe 100644 --- a/packages/eslint-plugin/src/rules/return-await.ts +++ b/packages/eslint-plugin/src/rules/return-await.ts @@ -21,11 +21,11 @@ export default util.createRule({ type: 'problem', messages: { nonPromiseAwait: - 'returning an awaited value that is not a promise is not allowed', + 'Returning an awaited value that is not a promise is not allowed.', disallowedPromiseAwait: - 'returning an awaited promise is not allowed in this context', + 'Returning an awaited promise is not allowed in this context.', requiredPromiseAwait: - 'returning an awaited promise is required in this context', + 'Returning an awaited promise is required in this context.', }, schema: [ { diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 414dbbcdd03d..febfaf23a79b 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -23,7 +23,7 @@ export default createRule({ messages: { switchIsNotExhaustive: 'Switch is not exhaustive. Cases not matched: {{missingBranches}}', - addMissingCases: 'Add branches for missing cases', + addMissingCases: 'Add branches for missing cases.', }, }, defaultOptions: [], diff --git a/packages/eslint-plugin/src/rules/typedef.ts b/packages/eslint-plugin/src/rules/typedef.ts index 7e6f4f14965c..9545364c839c 100644 --- a/packages/eslint-plugin/src/rules/typedef.ts +++ b/packages/eslint-plugin/src/rules/typedef.ts @@ -28,8 +28,8 @@ export default util.createRule<[Options], MessageIds>({ recommended: false, }, messages: { - expectedTypedef: 'expected a type annotation', - expectedTypedefNamed: 'expected {{name}} to have a type annotation', + expectedTypedef: 'Expected a type annotation.', + expectedTypedefNamed: 'Expected {{name}} to have a type annotation.', }, schema: [ { From f667ff1708d4ed28b7ea5beea742889da69a76d9 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 27 Apr 2020 07:22:14 +1200 Subject: [PATCH 5/9] feat(eslint-plugin): create `no-invalid-void-type` rule (#1847) --- packages/eslint-plugin/README.md | 1 + packages/eslint-plugin/ROADMAP.md | 3 +- .../docs/rules/no-invalid-void-type.md | 101 ++++ packages/eslint-plugin/src/configs/all.json | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/no-invalid-void-type.ts | 116 +++++ .../tests/rules/no-invalid-void-type.test.ts | 476 ++++++++++++++++++ 7 files changed, 699 insertions(+), 1 deletion(-) create mode 100644 packages/eslint-plugin/docs/rules/no-invalid-void-type.md create mode 100644 packages/eslint-plugin/src/rules/no-invalid-void-type.ts create mode 100644 packages/eslint-plugin/tests/rules/no-invalid-void-type.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index c4507267c6f0..a0b38fead9c5 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -119,6 +119,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@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-implied-eval`](./docs/rules/no-implied-eval.md) | Disallow the use of `eval()`-like methods | | | :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 | | | | | [`@typescript-eslint/no-misused-new`](./docs/rules/no-misused-new.md) | Enforce valid definition of `new` and `constructor` | :heavy_check_mark: | | | | [`@typescript-eslint/no-misused-promises`](./docs/rules/no-misused-promises.md) | Avoid using promises in places not designed to handle them | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-namespace`](./docs/rules/no-namespace.md) | Disallow the use of custom TypeScript modules and namespaces | :heavy_check_mark: | | | diff --git a/packages/eslint-plugin/ROADMAP.md b/packages/eslint-plugin/ROADMAP.md index c6348ab26588..ea2837727b33 100644 --- a/packages/eslint-plugin/ROADMAP.md +++ b/packages/eslint-plugin/ROADMAP.md @@ -18,7 +18,7 @@ It lists all TSLint rules along side rules from the ESLint ecosystem that are th | [`adjacent-overload-signatures`] | ✅ | [`@typescript-eslint/adjacent-overload-signatures`] | | [`ban-ts-ignore`] | ✅ | [`@typescript-eslint/ban-ts-ignore`] | | [`ban-types`] | 🌓 | [`@typescript-eslint/ban-types`][1] | -| [`invalid-void`] | 🛑 | N/A | +| [`invalid-void`] | ✅ | [`@typescript-eslint/no-invalid-void-type`] | | [`member-access`] | ✅ | [`@typescript-eslint/explicit-member-accessibility`] | | [`member-ordering`] | ✅ | [`@typescript-eslint/member-ordering`] | | [`no-any`] | ✅ | [`@typescript-eslint/no-explicit-any`] | @@ -623,6 +623,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint- [`@typescript-eslint/restrict-plus-operands`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/restrict-plus-operands.md [`@typescript-eslint/strict-boolean-expressions`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md [`@typescript-eslint/indent`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/indent.md +[`@typescript-eslint/no-invalid-void-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-invalid-void-type.md [`@typescript-eslint/no-require-imports`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-require-imports.md [`@typescript-eslint/array-type`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/array-type.md [`@typescript-eslint/class-name-casing`]: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/class-name-casing.md diff --git a/packages/eslint-plugin/docs/rules/no-invalid-void-type.md b/packages/eslint-plugin/docs/rules/no-invalid-void-type.md new file mode 100644 index 000000000000..9cc9808852f7 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-invalid-void-type.md @@ -0,0 +1,101 @@ +# Disallows usage of `void` type outside of generic or return types (`no-invalid-void-type`) + +Disallows usage of `void` type outside of return types or generic type arguments. +If `void` is used as return type, it shouldn’t be a part of intersection/union type. + +## Rationale + +The `void` type means “nothing” or that a function does not return any value, +in contrast with implicit `undefined` type which means that a function returns a value `undefined`. +So “nothing” cannot be mixed with any other types. If you need this - use the `undefined` type instead. + +## Rule Details + +This rule aims to ensure that the `void` type is only used in valid places. + +The following patterns are considered warnings: + +```ts +type PossibleValues = string | number | void; +type MorePossibleValues = string | ((number & any) | (string | void)); + +function logSomething(thing: void) {} +function printArg(arg: T) {} + +logAndReturn(undefined); + +interface Interface { + lambda: () => void; + prop: void; +} + +class MyClass { + private readonly propName: void; +} +``` + +The following patterns are not considered warnings: + +```ts +type NoOp = () => void; + +function noop(): void {} + +let trulyUndefined = void 0; + +async function promiseMeSomething(): Promise {} +``` + +### Options + +```ts +interface Options { + allowInGenericTypeArguments?: boolean | string[]; +} + +const defaultOptions: Options = { + allowInGenericTypeArguments: true, +}; +``` + +#### `allowInGenericTypeArguments` + +This option lets you control if `void` can be used as a valid value for generic type parameters. + +Alternatively, you can provide an array of strings which whitelist which types may accept `void` as a generic type parameter. + +This option is `true` by default. + +The following patterns are considered warnings with `{ allowInGenericTypeArguments: false }`: + +```ts +logAndReturn(undefined); + +let voidPromise: Promise = new Promise(() => {}); +let voidMap: Map = new Map(); +``` + +The following patterns are considered warnings with `{ allowInGenericTypeArguments: ['Ex.Mx.Tx'] }`: + +```ts +logAndReturn(undefined); + +type NotAllowedVoid1 = Mx.Tx; +type NotAllowedVoid2 = Tx; +type NotAllowedVoid3 = Promise; +``` + +The following patterns are not considered warnings with `{ allowInGenericTypeArguments: ['Ex.Mx.Tx'] }`: + +```ts +type AllowedVoid = Ex.MX.Tx; +``` + +## When Not To Use It + +If you don't care about if `void` is used with other types, +or in invalid places, then you don't need this rule. + +## Compatibility + +- TSLint: [invalid-void](https://palantir.github.io/tslint/rules/invalid-void/) diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index 99e6060d6709..7a6a217140a6 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -50,6 +50,7 @@ "@typescript-eslint/no-for-in-array": "error", "@typescript-eslint/no-implied-eval": "error", "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-invalid-void-type": "error", "no-magic-numbers": "off", "@typescript-eslint/no-magic-numbers": "error", "@typescript-eslint/no-misused-new": "error", diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index cbc3b8c68f9d..b6652f79eb4e 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -40,6 +40,7 @@ import noFloatingPromises from './no-floating-promises'; import noForInArray from './no-for-in-array'; import noImpliedEval from './no-implied-eval'; import noInferrableTypes from './no-inferrable-types'; +import noInvalidVoidType from './no-invalid-void-type'; import noMagicNumbers from './no-magic-numbers'; import noMisusedNew from './no-misused-new'; import noMisusedPromises from './no-misused-promises'; @@ -142,6 +143,7 @@ export default { 'no-for-in-array': noForInArray, 'no-implied-eval': noImpliedEval, 'no-inferrable-types': noInferrableTypes, + 'no-invalid-void-type': noInvalidVoidType, 'no-magic-numbers': noMagicNumbers, 'no-misused-new': noMisusedNew, 'no-misused-promises': noMisusedPromises, diff --git a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts new file mode 100644 index 000000000000..7d5dea300338 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts @@ -0,0 +1,116 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; +import * as util from '../util'; + +interface Options { + allowInGenericTypeArguments: boolean | string[]; +} + +type MessageIds = + | 'invalidVoidForGeneric' + | 'invalidVoidNotReturnOrGeneric' + | 'invalidVoidNotReturn'; + +export default util.createRule<[Options], MessageIds>({ + name: 'no-invalid-void-type', + meta: { + type: 'problem', + docs: { + description: + 'Disallows usage of `void` type outside of generic or return types', + category: 'Best Practices', + recommended: false, + }, + messages: { + invalidVoidForGeneric: + '{{ generic }} may not have void as a type variable', + invalidVoidNotReturnOrGeneric: + 'void is only valid as a return type or generic type variable', + invalidVoidNotReturn: 'void is only valid as a return type', + }, + schema: [ + { + type: 'object', + properties: { + allowInGenericTypeArguments: { + oneOf: [ + { type: 'boolean' }, + { + type: 'array', + items: { type: 'string' }, + minLength: 1, + }, + ], + }, + }, + additionalProperties: false, + }, + ], + }, + defaultOptions: [{ allowInGenericTypeArguments: true }], + create(context, [{ allowInGenericTypeArguments }]) { + const validParents: AST_NODE_TYPES[] = [ + AST_NODE_TYPES.TSTypeAnnotation, // + ]; + const invalidGrandParents: AST_NODE_TYPES[] = [ + AST_NODE_TYPES.TSPropertySignature, + AST_NODE_TYPES.CallExpression, + AST_NODE_TYPES.ClassProperty, + AST_NODE_TYPES.Identifier, + ]; + + if (allowInGenericTypeArguments === true) { + validParents.push(AST_NODE_TYPES.TSTypeParameterInstantiation); + } + + return { + TSVoidKeyword(node: TSESTree.TSVoidKeyword): void { + /* istanbul ignore next */ + if (!node.parent?.parent) { + return; + } + + if ( + validParents.includes(node.parent.type) && + !invalidGrandParents.includes(node.parent.parent.type) + ) { + return; + } + + if ( + node.parent.type === AST_NODE_TYPES.TSTypeParameterInstantiation && + node.parent.parent.type === AST_NODE_TYPES.TSTypeReference && + Array.isArray(allowInGenericTypeArguments) + ) { + const sourceCode = context.getSourceCode(); + const fullyQualifiedName = sourceCode + .getText(node.parent.parent.typeName) + .replace(/ /gu, ''); + + if ( + !allowInGenericTypeArguments + .map(s => s.replace(/ /gu, '')) + .includes(fullyQualifiedName) + ) { + context.report({ + messageId: 'invalidVoidForGeneric', + data: { generic: fullyQualifiedName }, + node, + }); + } + + return; + } + + context.report({ + messageId: allowInGenericTypeArguments + ? 'invalidVoidNotReturnOrGeneric' + : 'invalidVoidNotReturn', + node, + }); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-invalid-void-type.test.ts b/packages/eslint-plugin/tests/rules/no-invalid-void-type.test.ts new file mode 100644 index 000000000000..057da9b7e84a --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-invalid-void-type.test.ts @@ -0,0 +1,476 @@ +import rule from '../../src/rules/no-invalid-void-type'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('allowInGenericTypeArguments: false', rule, { + valid: [ + { + code: 'type Generic = [T];', + options: [{ allowInGenericTypeArguments: false }], + }, + ], + invalid: [ + { + code: 'type GenericVoid = Generic;', + options: [{ allowInGenericTypeArguments: false }], + errors: [ + { + messageId: 'invalidVoidNotReturn', + line: 1, + column: 28, + }, + ], + }, + { + code: 'function takeVoid(thing: void) {}', + options: [{ allowInGenericTypeArguments: false }], + errors: [ + { + messageId: 'invalidVoidNotReturn', + line: 1, + column: 26, + }, + ], + }, + { + code: 'let voidPromise: Promise = new Promise(() => {});', + options: [{ allowInGenericTypeArguments: false }], + errors: [ + { + messageId: 'invalidVoidNotReturn', + line: 1, + column: 26, + }, + { + messageId: 'invalidVoidNotReturn', + line: 1, + column: 46, + }, + ], + }, + { + code: 'let voidMap: Map = new Map();', + options: [{ allowInGenericTypeArguments: false }], + errors: [ + { + messageId: 'invalidVoidNotReturn', + line: 1, + column: 26, + }, + { + messageId: 'invalidVoidNotReturn', + line: 1, + column: 50, + }, + ], + }, + ], +}); + +ruleTester.run('allowInGenericTypeArguments: true', rule, { + valid: [ + 'function func(): void {}', + 'type NormalType = () => void;', + 'let normalArrow = (): void => {};', + 'let ughThisThing = void 0;', + 'function takeThing(thing: undefined) {}', + 'takeThing(void 0);', + 'let voidPromise: Promise = new Promise(() => {});', + 'let voidMap: Map = new Map();', + ` + function returnsVoidPromiseDirectly(): Promise { + return Promise.resolve(); + } + `, + 'async function returnsVoidPromiseAsync(): Promise {}', + 'type UnionType = string | number;', + 'type GenericVoid = Generic;', + 'type Generic = [T];', + ], + invalid: [ + { + code: 'function takeVoid(thing: void) {}', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 26, + }, + ], + }, + { + code: 'const arrowGeneric = (arg: T) => {};', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 33, + }, + ], + }, + { + code: 'const arrowGeneric1 = (arg: T) => {};', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 28, + }, + ], + }, + { + code: 'const arrowGeneric2 = (arg: T) => {};', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 34, + }, + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 41, + }, + ], + }, + { + code: 'function functionGeneric(arg: T) {}', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 36, + }, + ], + }, + { + code: 'function functionGeneric1(arg: T) {}', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 31, + }, + ], + }, + { + code: 'function functionGeneric2(arg: T) {}', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 37, + }, + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 44, + }, + ], + }, + { + code: + 'declare function functionDeclaration(arg: T): void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 48, + }, + ], + }, + { + code: 'declare function functionDeclaration1(arg: T): void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 43, + }, + ], + }, + { + code: + 'declare function functionDeclaration2(arg: T): void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 49, + }, + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 56, + }, + ], + }, + { + code: 'functionGeneric(undefined);', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 17, + }, + ], + }, + { + code: 'declare function voidArray(args: void[]): void[];', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 34, + }, + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 43, + }, + ], + }, + { + code: 'let value = undefined as void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 26, + }, + ], + }, + { + code: 'let value = undefined;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 14, + }, + ], + }, + { + code: 'function takesThings(...things: void[]): void {}', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 33, + }, + ], + }, + { + code: 'type KeyofVoid = keyof void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 24, + }, + ], + }, + { + code: ` + interface Interface { + lambda: () => void; + voidProp: void; + } + `, + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 4, + column: 21, + }, + ], + }, + { + code: ` + class ClassName { + private readonly propName: void; + } + `, + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 3, + column: 38, + }, + ], + }, + { + code: 'let letVoid: void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 14, + }, + ], + }, + { + code: ` + type VoidType = void; + + class OtherClassName { + private propName: VoidType; + } + `, + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 2, + column: 25, + }, + ], + }, + { + code: 'type UnionType2 = string | number | void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 37, + }, + ], + }, + { + code: 'type UnionType3 = string | ((number & any) | (string | void));', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 56, + }, + ], + }, + { + code: 'type IntersectionType = string & number & void;', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 43, + }, + ], + }, + { + code: ` + type MappedType = { + [K in keyof T]: void; + }; + `, + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 3, + column: 27, + }, + ], + }, + { + code: ` + type ConditionalType = { + [K in keyof T]: T[K] extends string ? void : string; + }; + `, + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 3, + column: 49, + }, + ], + }, + { + code: 'type ManyVoid = readonly void[];', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 26, + }, + ], + }, + { + code: 'function foo(arr: readonly void[]) {}', + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 28, + }, + ], + }, + ], +}); + +ruleTester.run('allowInGenericTypeArguments: whitelist', rule, { + valid: [ + 'type Allowed = [T];', + 'type Banned = [T];', + { + code: 'type AllowedVoid = Allowed;', + options: [{ allowInGenericTypeArguments: ['Allowed'] }], + }, + { + code: 'type AllowedVoid = Ex.Mx.Tx;', + options: [{ allowInGenericTypeArguments: ['Ex.Mx.Tx'] }], + }, + { + // eslint-disable-next-line @typescript-eslint/internal/plugin-test-formatting + code: 'type AllowedVoid = Ex . Mx . Tx;', + options: [{ allowInGenericTypeArguments: ['Ex.Mx.Tx'] }], + }, + { + // eslint-disable-next-line @typescript-eslint/internal/plugin-test-formatting + code: 'type AllowedVoid = Ex . Mx . Tx;', + options: [{ allowInGenericTypeArguments: ['Ex.Mx . Tx'] }], + }, + { + code: 'type AllowedVoid = Ex.Mx.Tx;', + options: [{ allowInGenericTypeArguments: ['Ex . Mx . Tx'] }], + }, + ], + invalid: [ + { + code: 'type BannedVoid = Banned;', + options: [{ allowInGenericTypeArguments: ['Allowed'] }], + errors: [ + { + messageId: 'invalidVoidForGeneric', + data: { generic: 'Banned' }, + line: 1, + column: 26, + }, + ], + }, + { + code: 'type BannedVoid = Ex.Mx.Tx;', + options: [{ allowInGenericTypeArguments: ['Tx'] }], + errors: [ + { + messageId: 'invalidVoidForGeneric', + data: { generic: 'Ex.Mx.Tx' }, + line: 1, + column: 28, + }, + ], + }, + { + code: 'function takeVoid(thing: void) {}', + options: [{ allowInGenericTypeArguments: ['Allowed'] }], + errors: [ + { + messageId: 'invalidVoidNotReturnOrGeneric', + line: 1, + column: 26, + }, + ], + }, + ], +}); From a85c3e1515d735b6c245cc658cdaec6deb05d630 Mon Sep 17 00:00:00 2001 From: Anix Date: Mon, 27 Apr 2020 01:11:31 +0530 Subject: [PATCH 6/9] feat(eslint-plugin): add extension rule `dot-notation` (#1867) --- packages/eslint-plugin/README.md | 1 + .../eslint-plugin/docs/rules/dot-notation.md | 46 ++++ packages/eslint-plugin/src/configs/all.json | 2 + .../eslint-plugin/src/rules/dot-notation.ts | 80 ++++++ packages/eslint-plugin/src/rules/index.ts | 2 + .../tests/rules/dot-notation.test.ts | 259 ++++++++++++++++++ .../eslint-plugin/typings/eslint-rules.d.ts | 19 ++ 7 files changed, 409 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/dot-notation.md create mode 100644 packages/eslint-plugin/src/rules/dot-notation.ts create mode 100644 packages/eslint-plugin/tests/rules/dot-notation.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index a0b38fead9c5..f6e70ef98acf 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -182,6 +182,7 @@ In these cases, we create what we call an extension rule; a rule within our plug | [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | | [`@typescript-eslint/comma-spacing`](./docs/rules/comma-spacing.md) | Enforces consistent spacing before and after commas | | :wrench: | | | [`@typescript-eslint/default-param-last`](./docs/rules/default-param-last.md) | Enforce default parameters to be last | | | | +| [`@typescript-eslint/dot-notation`](./docs/rules/dot-notation.md) | enforce dot notation whenever possible | | :wrench: | :thought_balloon: | | [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | | | [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | | :wrench: | | | [`@typescript-eslint/init-declarations`](./docs/rules/init-declarations.md) | require or disallow initialization in variable declarations | | | | diff --git a/packages/eslint-plugin/docs/rules/dot-notation.md b/packages/eslint-plugin/docs/rules/dot-notation.md new file mode 100644 index 000000000000..f827b003e57b --- /dev/null +++ b/packages/eslint-plugin/docs/rules/dot-notation.md @@ -0,0 +1,46 @@ +# enforce dot notation whenever possible (`dot-notation`) + +## Rule Details + +This rule extends the base [`eslint/dot-notation`](https://eslint.org/docs/rules/dot-notation) rule. +It adds support for optionally ignoring computed `private` member access. + +## How to use + +```cjson +{ + // note you must disable the base rule as it can report incorrect errors + "dot-notation": "off", + "@typescript-eslint/dot-notation": ["error"] +} +``` + +## Options + +See [`eslint/dot-notation`](https://eslint.org/docs/rules/dot-notation#options) options. +This rule adds the following options: + +```ts +interface Options extends BaseDotNotationOptions { + allowPrivateClassPropertyAccess?: boolean; +} +const defaultOptions: Options = { + ...baseDotNotationDefaultOptions, + allowPrivateClassPropertyAccess: false, +}; +``` + +### `allowPrivateClassPropertyAccess` + +Example of a correct code when `allowPrivateClassPropertyAccess` is set to `true` + +```ts +class X { + private priv_prop = 123; +} + +const x = new X(); +x['priv_prop'] = 123; +``` + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/dot-notation.md) diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index 7a6a217140a6..0e103162f5f5 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -15,6 +15,8 @@ "@typescript-eslint/consistent-type-definitions": "error", "default-param-last": "off", "@typescript-eslint/default-param-last": "error", + "dot-notation": "off", + "@typescript-eslint/dot-notation": "error", "@typescript-eslint/explicit-function-return-type": "error", "@typescript-eslint/explicit-member-accessibility": "error", "@typescript-eslint/explicit-module-boundary-types": "error", diff --git a/packages/eslint-plugin/src/rules/dot-notation.ts b/packages/eslint-plugin/src/rules/dot-notation.ts new file mode 100644 index 000000000000..fb710187d294 --- /dev/null +++ b/packages/eslint-plugin/src/rules/dot-notation.ts @@ -0,0 +1,80 @@ +import { TSESTree } from '@typescript-eslint/experimental-utils'; +import * as ts from 'typescript'; +import baseRule from 'eslint/lib/rules/dot-notation'; +import { + InferOptionsTypeFromRule, + InferMessageIdsTypeFromRule, + createRule, + getParserServices, +} from '../util'; + +export type Options = InferOptionsTypeFromRule; +export type MessageIds = InferMessageIdsTypeFromRule; + +export default createRule({ + name: 'dot-notation', + meta: { + type: 'suggestion', + docs: { + description: 'enforce dot notation whenever possible', + category: 'Best Practices', + recommended: false, + extendsBaseRule: true, + requiresTypeChecking: true, + }, + schema: [ + { + type: 'object', + properties: { + allowKeywords: { + type: 'boolean', + default: true, + }, + allowPattern: { + type: 'string', + default: '', + }, + allowPrivateClassPropertyAccess: { + tyoe: 'boolean', + default: false, + }, + }, + additionalProperties: false, + }, + ], + fixable: baseRule.meta.fixable, + messages: baseRule.meta.messages, + }, + defaultOptions: [ + { + allowPrivateClassPropertyAccess: false, + allowKeywords: true, + allowPattern: '', + }, + ], + create(context, [options]) { + const rules = baseRule.create(context); + const allowPrivateClassPropertyAccess = + options.allowPrivateClassPropertyAccess; + + const parserServices = getParserServices(context); + const typeChecker = parserServices.program.getTypeChecker(); + + return { + MemberExpression(node: TSESTree.MemberExpression): void { + const objectSymbol = typeChecker.getSymbolAtLocation( + parserServices.esTreeNodeToTSNodeMap.get(node.property), + ); + + if ( + allowPrivateClassPropertyAccess && + objectSymbol?.declarations[0]?.modifiers?.[0].kind === + ts.SyntaxKind.PrivateKeyword + ) { + return; + } + rules.MemberExpression(node); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index b6652f79eb4e..2d8e6830c4ff 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -12,6 +12,7 @@ import commaSpacing from './comma-spacing'; import consistentTypeAssertions from './consistent-type-assertions'; import consistentTypeDefinitions from './consistent-type-definitions'; import defaultParamLast from './default-param-last'; +import dotNotation from './dot-notation'; import explicitFunctionReturnType from './explicit-function-return-type'; import explicitMemberAccessibility from './explicit-member-accessibility'; import explicitModuleBoundaryTypes from './explicit-module-boundary-types'; @@ -114,6 +115,7 @@ export default { 'consistent-type-assertions': consistentTypeAssertions, 'consistent-type-definitions': consistentTypeDefinitions, 'default-param-last': defaultParamLast, + 'dot-notation': dotNotation, 'explicit-function-return-type': explicitFunctionReturnType, 'explicit-member-accessibility': explicitMemberAccessibility, 'explicit-module-boundary-types': explicitModuleBoundaryTypes, diff --git a/packages/eslint-plugin/tests/rules/dot-notation.test.ts b/packages/eslint-plugin/tests/rules/dot-notation.test.ts new file mode 100644 index 000000000000..797b111cb173 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/dot-notation.test.ts @@ -0,0 +1,259 @@ +import rule from '../../src/rules/dot-notation'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; + +const rootPath = getFixturesRootDir(); + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + tsconfigRootDir: rootPath, + project: './tsconfig.json', + }, +}); + +/** + * Quote a string in "double quotes" because it’s painful + * with a double-quoted string literal + */ +function q(str: string): string { + return `"${str}"`; +} + +ruleTester.run('dot-notation', rule, { + valid: [ + // baseRule + + 'a.b;', + 'a.b.c;', + "a['12'];", + 'a[b];', + 'a[0];', + { code: 'a.b.c;', options: [{ allowKeywords: false }] }, + { code: 'a.arguments;', options: [{ allowKeywords: false }] }, + { code: 'a.let;', options: [{ allowKeywords: false }] }, + { code: 'a.yield;', options: [{ allowKeywords: false }] }, + { code: 'a.eval;', options: [{ allowKeywords: false }] }, + { code: 'a[0];', options: [{ allowKeywords: false }] }, + { code: "a['while'];", options: [{ allowKeywords: false }] }, + { code: "a['true'];", options: [{ allowKeywords: false }] }, + { code: "a['null'];", options: [{ allowKeywords: false }] }, + { code: 'a[true];', options: [{ allowKeywords: false }] }, + { code: 'a[null];', options: [{ allowKeywords: false }] }, + { code: 'a.true;', options: [{ allowKeywords: true }] }, + { code: 'a.null;', options: [{ allowKeywords: true }] }, + { + code: "a['snake_case'];", + options: [{ allowPattern: '^[a-z]+(_[a-z]+)+$' }], + }, + { + code: "a['lots_of_snake_case'];", + options: [{ allowPattern: '^[a-z]+(_[a-z]+)+$' }], + }, + { code: 'a[`time${range}`];', parserOptions: { ecmaVersion: 6 } }, + { + code: 'a[`while`];', + options: [{ allowKeywords: false }], + parserOptions: { ecmaVersion: 6 }, + }, + { code: 'a[`time range`];', parserOptions: { ecmaVersion: 6 } }, + 'a.true;', + 'a.null;', + 'a[undefined];', + 'a[void 0];', + 'a[b()];', + { code: 'a[/(?0)/];', parserOptions: { ecmaVersion: 2018 } }, + + { + code: ` +class X { + private priv_prop = 123; +} + +const x = new X(); +x['priv_prop'] = 123; + `, + options: [{ allowPrivateClassPropertyAccess: true }], + }, + ], + invalid: [ + { + code: ` +class X { + private priv_prop = 123; +} + +const x = new X(); +x['priv_prop'] = 123; + `, + options: [{ allowPrivateClassPropertyAccess: false }], + output: ` +class X { + private priv_prop = 123; +} + +const x = new X(); +x.priv_prop = 123; + `, + errors: [{ messageId: 'useDot' }], + }, + { + code: ` +class X { + public pub_prop = 123; +} + +const x = new X(); +x['pub_prop'] = 123; + `, + output: ` +class X { + public pub_prop = 123; +} + +const x = new X(); +x.pub_prop = 123; + `, + errors: [{ messageId: 'useDot' }], + }, + // baseRule + + // { + // code: 'a.true;', + // output: "a['true'];", + // options: [{ allowKeywords: false }], + // errors: [{ messageId: "useBrackets", data: { key: "true" } }], + // }, + { + code: "a['true'];", + output: 'a.true;', + errors: [{ messageId: 'useDot', data: { key: q('true') } }], + }, + { + code: "a['time'];", + output: 'a.time;', + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'useDot', data: { key: '"time"' } }], + }, + { + code: 'a[null];', + output: 'a.null;', + errors: [{ messageId: 'useDot', data: { key: 'null' } }], + }, + { + code: 'a[true];', + output: 'a.true;', + errors: [{ messageId: 'useDot', data: { key: 'true' } }], + }, + { + code: 'a[false];', + output: 'a.false;', + errors: [{ messageId: 'useDot', data: { key: 'false' } }], + }, + { + code: "a['b'];", + output: 'a.b;', + errors: [{ messageId: 'useDot', data: { key: q('b') } }], + }, + { + code: "a.b['c'];", + output: 'a.b.c;', + errors: [{ messageId: 'useDot', data: { key: q('c') } }], + }, + { + code: "a['_dangle'];", + output: 'a._dangle;', + options: [{ allowPattern: '^[a-z]+(_[a-z]+)+$' }], + errors: [{ messageId: 'useDot', data: { key: q('_dangle') } }], + }, + { + code: "a['SHOUT_CASE'];", + output: 'a.SHOUT_CASE;', + options: [{ allowPattern: '^[a-z]+(_[a-z]+)+$' }], + errors: [{ messageId: 'useDot', data: { key: q('SHOUT_CASE') } }], + }, + { + code: 'a\n' + " ['SHOUT_CASE'];", + output: 'a\n' + ' .SHOUT_CASE;', + errors: [ + { + messageId: 'useDot', + data: { key: q('SHOUT_CASE') }, + line: 2, + column: 4, + }, + ], + }, + { + code: + 'getResource()\n' + + ' .then(function(){})\n' + + ' ["catch"](function(){})\n' + + ' .then(function(){})\n' + + ' ["catch"](function(){});', + output: + 'getResource()\n' + + ' .then(function(){})\n' + + ' .catch(function(){})\n' + + ' .then(function(){})\n' + + ' .catch(function(){});', + errors: [ + { + messageId: 'useDot', + data: { key: q('catch') }, + line: 3, + column: 6, + }, + { + messageId: 'useDot', + data: { key: q('catch') }, + line: 5, + column: 6, + }, + ], + }, + { + code: 'foo\n' + ' .while;', + output: 'foo\n' + ' ["while"];', + options: [{ allowKeywords: false }], + errors: [{ messageId: 'useBrackets', data: { key: 'while' } }], + }, + { + code: "foo[/* comment */ 'bar'];", + output: null, // Not fixed due to comment + errors: [{ messageId: 'useDot', data: { key: q('bar') } }], + }, + { + code: "foo['bar' /* comment */];", + output: null, // Not fixed due to comment + errors: [{ messageId: 'useDot', data: { key: q('bar') } }], + }, + { + code: "foo['bar'];", + output: 'foo.bar;', + errors: [{ messageId: 'useDot', data: { key: q('bar') } }], + }, + { + code: 'foo./* comment */ while;', + output: null, // Not fixed due to comment + options: [{ allowKeywords: false }], + errors: [{ messageId: 'useBrackets', data: { key: 'while' } }], + }, + { + code: 'foo[null];', + output: 'foo.null;', + errors: [{ messageId: 'useDot', data: { key: 'null' } }], + }, + { + code: "foo['bar'] instanceof baz;", + output: 'foo.bar instanceof baz;', + errors: [{ messageId: 'useDot', data: { key: q('bar') } }], + }, + { + code: 'let.if();', + output: null, // `let["if"]()` is a syntax error because `let[` indicates a destructuring variable declaration + options: [{ allowKeywords: false }], + errors: [{ messageId: 'useBrackets', data: { key: 'if' } }], + }, + ], +}); diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index eb2d1cd9c60e..0bac88823e2d 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -640,3 +640,22 @@ declare module 'eslint/lib/rules/init-declarations' { >; export = rule; } + +declare module 'eslint/lib/rules/dot-notation' { + import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; + + const rule: TSESLint.RuleModule< + 'useDot' | 'useBrackets', + [ + { + allowKeywords?: boolean; + allowPattern?: string; + allowPrivateClassPropertyAccess?: boolean; + }, + ], + { + MemberExpression(node: TSESTree.MemberExpression): void; + } + >; + export = rule; +} From ed2bd6067f74ae33e36a084719bb91efedfba599 Mon Sep 17 00:00:00 2001 From: sinyovercosy Date: Sun, 26 Apr 2020 20:23:46 -0500 Subject: [PATCH 7/9] fix(eslint-plugin): [prefer-string-starts-ends-with] check for negative start index in slice (#1920) --- .../rules/prefer-string-starts-ends-with.ts | 31 +++++++++++++++++-- .../prefer-string-starts-ends-with.test.ts | 8 ++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts index 217eada2e85d..795c877a8e3d 100644 --- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts +++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts @@ -166,6 +166,27 @@ export default createRule({ ); } + /** + * Check if a given node is a negative index expression + * + * E.g. `s.slice(- )`, `s.substring(s.length - )` + * + * @param node The node to check. + * @param expectedIndexedNode The node which is expected as the receiver of index expression. + */ + function isNegativeIndexExpression( + node: TSESTree.Node, + expectedIndexedNode: TSESTree.Node, + ): boolean { + return ( + (node.type === AST_NODE_TYPES.UnaryExpression && + node.operator === '-') || + (node.type === AST_NODE_TYPES.BinaryExpression && + node.operator === '-' && + isLengthExpression(node.left, expectedIndexedNode)) + ); + } + /** * Check if a given node is the expression of the last index. * @@ -538,9 +559,10 @@ export default createRule({ } const isEndsWith = - callNode.arguments.length === 1 || - (callNode.arguments.length === 2 && - isLengthExpression(callNode.arguments[1], node.object)); + (callNode.arguments.length === 1 || + (callNode.arguments.length === 2 && + isLengthExpression(callNode.arguments[1], node.object))) && + isNegativeIndexExpression(callNode.arguments[0], node.object); const isStartsWith = !isEndsWith && callNode.arguments.length === 2 && @@ -564,6 +586,9 @@ export default createRule({ ) { return null; } + // code being checked is likely mistake: + // unequal length of strings being checked for equality + // or reliant on behavior of substring (negative indices interpreted as 0) if (isStartsWith) { if (!isLengthExpression(callNode.arguments[1], eqNode.right)) { return null; diff --git a/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts b/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts index 0022c37cb2f7..e97fa912df5b 100644 --- a/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-string-starts-ends-with.test.ts @@ -209,6 +209,12 @@ ruleTester.run('prefer-string-starts-ends-with', rule, { s.slice(-4, -1) === "bar" } `, + // https://github.com/typescript-eslint/typescript-eslint/issues/1690 + ` + function f(s: string) { + s.slice(1) === "bar" + } + `, ` function f(s: string) { pattern.test(s) @@ -872,7 +878,7 @@ ruleTester.run('prefer-string-starts-ends-with', rule, { { code: ` function f(s: string) { - s.slice(startIndex) === needle // 'startIndex' can be different + s.slice(-length) === needle // 'length' can be different } `, output: null, From 2f45e9992a8f12b6233716e77a6159f9cea2c879 Mon Sep 17 00:00:00 2001 From: duduluu Date: Mon, 27 Apr 2020 18:08:13 +0800 Subject: [PATCH 8/9] fix(eslint-plugin): fix no-base-to-string boolean literal check (#1850) --- .../docs/rules/no-base-to-string.md | 28 ++++++++ .../src/rules/no-base-to-string.ts | 26 ++++++- .../tests/rules/no-base-to-string.test.ts | 70 ++++++++++++------- 3 files changed, 97 insertions(+), 27 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-base-to-string.md b/packages/eslint-plugin/docs/rules/no-base-to-string.md index 569ffe0e6d97..5c74cb4d84cd 100644 --- a/packages/eslint-plugin/docs/rules/no-base-to-string.md +++ b/packages/eslint-plugin/docs/rules/no-base-to-string.md @@ -52,6 +52,34 @@ const literalWithToString = { `Value: ${literalWithToString}`; ``` +## Options + +```ts +type Options = { + ignoredTypeNames?: string[]; +}; + +const defaultOptions: Options = { + ignoredTypeNames: ['RegExp'], +}; +``` + +### `ignoreTypeNames` + +A string array of type names to ignore, this is useful for types missing `toString()` (but actually has `toString()`). +There are some types missing `toString()` in old version TypeScript, like `RegExp`, `URL`, `URLSearchParams` etc. + +The following patterns are considered correct with the default options `{ ignoreTypeNames: ["RegExp"] }`: + +```ts +`${/regex/}`; +'' + /regex/; +/regex/.toString(); +let value = /regex/; +value.toString(); +let text = `${value}`; +``` + ## When Not To Use It If you don't mind `"[object Object]"` in your strings, then you will not need this rule. diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index a1121f37abd7..2e06bdb870d9 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -16,6 +16,7 @@ type Options = [ { /** @deprecated This option is now ignored and treated as always true, it will be removed in 3.0 */ ignoreTaggedTemplateExpressions?: boolean; + ignoredTypeNames?: string[]; }, ]; type MessageIds = 'baseToString'; @@ -42,16 +43,28 @@ export default util.createRule({ type: 'boolean', default: true, }, + ignoredTypeNames: { + type: 'array', + items: { + type: 'string', + }, + }, }, additionalProperties: false, }, ], type: 'suggestion', }, - defaultOptions: [{ ignoreTaggedTemplateExpressions: true }], - create(context) { + defaultOptions: [ + { + ignoreTaggedTemplateExpressions: true, + ignoredTypeNames: ['RegExp'], + }, + ], + create(context, [option]) { const parserServices = util.getParserServices(context); const typeChecker = parserServices.program.getTypeChecker(); + const ignoredTypeNames = option.ignoredTypeNames ?? []; function checkExpression(node: TSESTree.Expression, type?: ts.Type): void { if (node.type === AST_NODE_TYPES.Literal) { @@ -84,6 +97,15 @@ export default util.createRule({ return Usefulness.Always; } + // Patch for old version TypeScript, the Boolean type definition missing toString() + if (type.flags & ts.TypeFlags.BooleanLiteral) { + return Usefulness.Always; + } + + if (ignoredTypeNames.includes(util.getTypeName(typeChecker, type))) { + return Usefulness.Always; + } + if ( toString.declarations.every( ({ parent }) => diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index 82bd4e0302e1..842ead488c1b 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -11,39 +11,59 @@ const ruleTester = new RuleTester({ }, }); +const literalListBasic: string[] = [ + "''", + "'text'", + 'true', + 'false', + '1', + '1n', + '[]', + '/regex/', +]; + +const literalListNeedParen: string[] = [ + '{}.constructor()', + '() => {}', + 'function() {}', +]; + +const literalList = [...literalListBasic, ...literalListNeedParen]; + +const literalListWrapped = [ + ...literalListBasic, + ...literalListNeedParen.map(i => `(${i})`), +]; + ruleTester.run('no-base-to-string', rule, { valid: [ - "`${''}`;", - '`${true}`;', - '`${[]}`;', - '`${function() {}}`;', - "'' + '';", - "'' + true;", - "'' + [];", - 'true + true;', - "true + '';", - 'true + [];', - '[] + [];', - '[] + true;', - "[] + '';", - '({}.constructor());', - "'text'.toString();", - 'false.toString();', - ` -let value = 1; -value.toString(); - `, - ` -let value = 1n; -value.toString(); - `, + // template + ...literalList.map(i => `\`\${${i}}\`;`), + + // operator + += + ...literalListWrapped + .map(l => literalListWrapped.map(r => `${l} + ${r};`)) + .reduce((pre, cur) => [...pre, ...cur]), + + // toString() + ...literalListWrapped.map(i => `${i === '1' ? `(${i})` : i}.toString();`), + + // variable toString() and template + ...literalList.map( + i => ` + let value = ${i}; + value.toString(); + let text = \`\${value}\`; + `, + ), + ` function someFunction() {} someFunction.toString(); +let text = \`\${someFunction}\`; `, 'unknownObject.toString();', 'unknownObject.someOtherMethod();', - '(() => {}).toString();', ` class CustomToString { toString() { From 1b4e4301de6cc1d7e651d3cb5bffc9cfa567d39d Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 27 Apr 2020 17:02:00 +0000 Subject: [PATCH 9/9] chore: publish v2.30.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 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 | 11 +++++++++++ packages/experimental-utils/package.json | 4 ++-- packages/parser/CHANGELOG.md | 8 ++++++++ packages/parser/package.json | 8 ++++---- packages/shared-fixtures/CHANGELOG.md | 8 ++++++++ packages/shared-fixtures/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 8 ++++++++ packages/typescript-estree/package.json | 4 ++-- 16 files changed, 105 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0921927763f4..22869a97d6a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + + +### Bug Fixes + +* **eslint-plugin:** [prefer-string-starts-ends-with] check for negative start index in slice ([#1920](https://github.com/typescript-eslint/typescript-eslint/issues/1920)) ([ed2bd60](https://github.com/typescript-eslint/typescript-eslint/commit/ed2bd6067f74ae33e36a084719bb91efedfba599)) +* **eslint-plugin:** fix no-base-to-string boolean literal check ([#1850](https://github.com/typescript-eslint/typescript-eslint/issues/1850)) ([2f45e99](https://github.com/typescript-eslint/typescript-eslint/commit/2f45e9992a8f12b6233716e77a6159f9cea2c879)) + + +### Features + +* **eslint-plugin:** add extension rule `dot-notation` ([#1867](https://github.com/typescript-eslint/typescript-eslint/issues/1867)) ([a85c3e1](https://github.com/typescript-eslint/typescript-eslint/commit/a85c3e1515d735b6c245cc658cdaec6deb05d630)) +* **eslint-plugin:** create `no-invalid-void-type` rule ([#1847](https://github.com/typescript-eslint/typescript-eslint/issues/1847)) ([f667ff1](https://github.com/typescript-eslint/typescript-eslint/commit/f667ff1708d4ed28b7ea5beea742889da69a76d9)) +* **experimental-utils:** allow rule options to be a readonly tuple ([#1924](https://github.com/typescript-eslint/typescript-eslint/issues/1924)) ([4ef6788](https://github.com/typescript-eslint/typescript-eslint/commit/4ef67884962b6aac61cc895aaa3ba16aa892ecf4)) + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) diff --git a/lerna.json b/lerna.json index 3c1dacefef4d..167cbfa8d020 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.29.0", + "version": "2.30.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index 1634ffba2fa8..9f692a897a39 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 6348c281ab4e..ee0b7eb38f0a 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-internal", - "version": "2.29.0", + "version": "2.30.0", "private": true, "main": "dist/index.js", "scripts": { @@ -12,7 +12,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.29.0", + "@typescript-eslint/experimental-utils": "2.30.0", "prettier": "*" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index d43881609278..4302eb8a4316 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index fa6905b5a4f0..18d86d07b291 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "2.29.0", + "version": "2.30.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -31,7 +31,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.29.0", + "@typescript-eslint/experimental-utils": "2.30.0", "lodash": "^4.17.15" }, "peerDependencies": { @@ -41,6 +41,6 @@ }, "devDependencies": { "@types/lodash": "^4.14.149", - "@typescript-eslint/parser": "2.29.0" + "@typescript-eslint/parser": "2.30.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 37883be34309..72d1ddffdb6e 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + + +### Bug Fixes + +* **eslint-plugin:** [prefer-string-starts-ends-with] check for negative start index in slice ([#1920](https://github.com/typescript-eslint/typescript-eslint/issues/1920)) ([ed2bd60](https://github.com/typescript-eslint/typescript-eslint/commit/ed2bd6067f74ae33e36a084719bb91efedfba599)) +* **eslint-plugin:** fix no-base-to-string boolean literal check ([#1850](https://github.com/typescript-eslint/typescript-eslint/issues/1850)) ([2f45e99](https://github.com/typescript-eslint/typescript-eslint/commit/2f45e9992a8f12b6233716e77a6159f9cea2c879)) + + +### Features + +* **eslint-plugin:** add extension rule `dot-notation` ([#1867](https://github.com/typescript-eslint/typescript-eslint/issues/1867)) ([a85c3e1](https://github.com/typescript-eslint/typescript-eslint/commit/a85c3e1515d735b6c245cc658cdaec6deb05d630)) +* **eslint-plugin:** create `no-invalid-void-type` rule ([#1847](https://github.com/typescript-eslint/typescript-eslint/issues/1847)) ([f667ff1](https://github.com/typescript-eslint/typescript-eslint/commit/f667ff1708d4ed28b7ea5beea742889da69a76d9)) + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 29b9e0d308cd..6c359a6dd971 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "2.29.0", + "version": "2.30.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -41,7 +41,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.29.0", + "@typescript-eslint/experimental-utils": "2.30.0", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", "tsutils": "^3.17.1" diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index b1f38a523313..5827d7579f87 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + + +### Features + +* **experimental-utils:** allow rule options to be a readonly tuple ([#1924](https://github.com/typescript-eslint/typescript-eslint/issues/1924)) ([4ef6788](https://github.com/typescript-eslint/typescript-eslint/commit/4ef67884962b6aac61cc895aaa3ba16aa892ecf4)) + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) **Note:** Version bump only for package @typescript-eslint/experimental-utils diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index ac9a07b11c45..afeb51058c92 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "2.29.0", + "version": "2.30.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -37,7 +37,7 @@ }, "dependencies": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.29.0", + "@typescript-eslint/typescript-estree": "2.30.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" }, diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 74cb4ee49709..0a719082c0bb 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) **Note:** Version bump only for package @typescript-eslint/parser diff --git a/packages/parser/package.json b/packages/parser/package.json index 8aa5d8930483..f0c96b1123da 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "2.29.0", + "version": "2.30.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -43,13 +43,13 @@ }, "dependencies": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.29.0", - "@typescript-eslint/typescript-estree": "2.29.0", + "@typescript-eslint/experimental-utils": "2.30.0", + "@typescript-eslint/typescript-estree": "2.30.0", "eslint-visitor-keys": "^1.1.0" }, "devDependencies": { "@types/glob": "^7.1.1", - "@typescript-eslint/shared-fixtures": "2.29.0", + "@typescript-eslint/shared-fixtures": "2.30.0", "glob": "*" }, "peerDependenciesMeta": { diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 181fc307a111..1ed48f55f23a 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) **Note:** Version bump only for package @typescript-eslint/shared-fixtures diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index 610efbaabd73..92ec0038c52e 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "2.29.0", + "version": "2.30.0", "private": true, "scripts": { "build": "tsc -b tsconfig.build.json", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index 9ea9a9c6ec7a..e6f4401fc97c 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.30.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.29.0...v2.30.0) (2020-04-27) + +**Note:** Version bump only for package @typescript-eslint/typescript-estree + + + + + # [2.29.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.28.0...v2.29.0) (2020-04-20) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 8f69a2a71a0b..68c88aa160ed 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "2.29.0", + "version": "2.30.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -58,7 +58,7 @@ "@types/lodash": "^4.14.149", "@types/semver": "^6.2.0", "@types/tmp": "^0.1.0", - "@typescript-eslint/shared-fixtures": "2.29.0", + "@typescript-eslint/shared-fixtures": "2.30.0", "tmp": "^0.1.0", "typescript": "*" },