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/README.md b/packages/eslint-plugin/README.md
index c4507267c6f0..f6e70ef98acf 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: | | |
@@ -181,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/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/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/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 = {
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/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;
};
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/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/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json
index 99e6060d6709..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",
@@ -50,6 +52,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/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/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 cbc3b8c68f9d..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';
@@ -40,6 +41,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';
@@ -113,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,
@@ -142,6 +145,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/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-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/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-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/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/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/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: [
{
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/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() {
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,
+ },
+ ],
+ },
+ ],
+});
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,
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;
+}
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/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 };
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": "*"
},