diff --git a/.cspell.json b/.cspell.json index b3e10009914b..39e8c795e7af 100644 --- a/.cspell.json +++ b/.cspell.json @@ -9,7 +9,7 @@ "**/**/CHANGELOG.md", "**/**/CONTRIBUTORS.md", "**/**/ROADMAP.md", - "**/*.json" + "**/*.{json,snap}" ], "dictionaries": [ "typescript", diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f008a8e461c..a36543570994 100644 --- a/CHANGELOG.md +++ b/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.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + + +### Bug Fixes + +* **eslint-plugin:** crash in no-unnecessary-type-arguments ([#1401](https://github.com/typescript-eslint/typescript-eslint/issues/1401)) ([01c939f](https://github.com/typescript-eslint/typescript-eslint/commit/01c939f)) +* **typescript-estree:** correct persisted parse for windows ([#1406](https://github.com/typescript-eslint/typescript-eslint/issues/1406)) ([1a42f3d](https://github.com/typescript-eslint/typescript-eslint/commit/1a42f3d)) + + +### Features + +* **eslint-plugin:** [strict-bool-expr] add allowSafe option ([#1385](https://github.com/typescript-eslint/typescript-eslint/issues/1385)) ([9344233](https://github.com/typescript-eslint/typescript-eslint/commit/9344233)) +* **eslint-plugin:** add no-implied-eval ([#1375](https://github.com/typescript-eslint/typescript-eslint/issues/1375)) ([254d276](https://github.com/typescript-eslint/typescript-eslint/commit/254d276)) + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) diff --git a/lerna.json b/lerna.json index 10c3ad308134..1363f6a2904d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.14.0", + "version": "2.15.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index dfc1e0a77238..4d6e597d1ffb 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.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 5b960acef3c0..9d35aef53a87 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.14.0", + "version": "2.15.0", "private": true, "main": "dist/index.js", "scripts": { @@ -12,6 +12,6 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.14.0" + "@typescript-eslint/experimental-utils": "2.15.0" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index 2493b23fbc8e..e7ed64869469 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.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) **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 70fcea181282..b85ae6a5d558 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.14.0", + "version": "2.15.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.14.0", + "@typescript-eslint/experimental-utils": "2.15.0", "lodash.memoize": "^4.1.2" }, "peerDependencies": { @@ -41,6 +41,6 @@ }, "devDependencies": { "@types/lodash.memoize": "^4.1.4", - "@typescript-eslint/parser": "2.14.0" + "@typescript-eslint/parser": "2.15.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 19b45ab9e82d..3ecaf883e0e9 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + + +### Bug Fixes + +* **eslint-plugin:** crash in no-unnecessary-type-arguments ([#1401](https://github.com/typescript-eslint/typescript-eslint/issues/1401)) ([01c939f](https://github.com/typescript-eslint/typescript-eslint/commit/01c939f)) + + +### Features + +* **eslint-plugin:** [strict-bool-expr] add allowSafe option ([#1385](https://github.com/typescript-eslint/typescript-eslint/issues/1385)) ([9344233](https://github.com/typescript-eslint/typescript-eslint/commit/9344233)) +* **eslint-plugin:** add no-implied-eval ([#1375](https://github.com/typescript-eslint/typescript-eslint/issues/1375)) ([254d276](https://github.com/typescript-eslint/typescript-eslint/commit/254d276)) + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 586d43d0aa08..a332bafec80c 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -103,7 +103,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | | [`@typescript-eslint/camelcase`](./docs/rules/camelcase.md) | Enforce camelCase naming convention | :heavy_check_mark: | | | | [`@typescript-eslint/class-name-casing`](./docs/rules/class-name-casing.md) | Require PascalCased class and interface names | :heavy_check_mark: | | | -| [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions. | :heavy_check_mark: | | | +| [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions | :heavy_check_mark: | | | | [`@typescript-eslint/consistent-type-definitions`](./docs/rules/consistent-type-definitions.md) | Consistent with type definition either `interface` or `type` | | :wrench: | | | [`@typescript-eslint/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) | Require explicit return types on functions and class methods | :heavy_check_mark: | | | | [`@typescript-eslint/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) | Require explicit accessibility modifiers on class properties and methods | | | | @@ -115,18 +115,19 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/member-naming`](./docs/rules/member-naming.md) | Enforces naming conventions for class members by visibility | | | | | [`@typescript-eslint/member-ordering`](./docs/rules/member-ordering.md) | Require a consistent member declaration order | | | | | [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :heavy_check_mark: | :wrench: | | -| [`@typescript-eslint/no-dynamic-delete`](./docs/rules/no-dynamic-delete.md) | Bans usage of the delete operator with computed key expressions | | :wrench: | | +| [`@typescript-eslint/no-dynamic-delete`](./docs/rules/no-dynamic-delete.md) | Disallow the delete operator with computed key expressions | | :wrench: | | | [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :heavy_check_mark: | | | -| [`@typescript-eslint/no-empty-interface`](./docs/rules/no-empty-interface.md) | Disallow the declaration of empty interfaces | :heavy_check_mark: | :wrench: | +| [`@typescript-eslint/no-empty-interface`](./docs/rules/no-empty-interface.md) | Disallow the declaration of empty interfaces | :heavy_check_mark: | :wrench: | | | [`@typescript-eslint/no-explicit-any`](./docs/rules/no-explicit-any.md) | Disallow usage of the `any` type | :heavy_check_mark: | :wrench: | | -| [`@typescript-eslint/no-extra-non-null-assertion`](./docs/rules/no-extra-non-null-assertion.md) | Disallow extra non-null assertion | | | | +| [`@typescript-eslint/no-extra-non-null-assertion`](./docs/rules/no-extra-non-null-assertion.md) | Disallow extra non-null assertion | | | | | [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | -| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | | :wrench: | | +| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | | :wrench: | | | [`@typescript-eslint/no-extraneous-class`](./docs/rules/no-extraneous-class.md) | Forbids the use of classes as namespaces | | | | -| [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Requires Promise-like values to be handled appropriately. | | | :thought_balloon: | +| [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Requires Promise-like values to be handled appropriately | | | :thought_balloon: | | [`@typescript-eslint/no-for-in-array`](./docs/rules/no-for-in-array.md) | Disallow iterating over an array with a for-in loop | :heavy_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/no-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-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallows magic numbers | | | | +| [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallow magic numbers | | | | | [`@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: | | | @@ -134,16 +135,16 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | | | | | [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallows invocation of `require()` | | | | | [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | :heavy_check_mark: | | | -| [`@typescript-eslint/no-throw-literal`](./docs/rules/no-throw-literal.md) | Disallow throwing literals as exceptions | | | :thought_balloon: | +| [`@typescript-eslint/no-throw-literal`](./docs/rules/no-throw-literal.md) | Disallow throwing literals as exceptions | | | :thought_balloon: | | [`@typescript-eslint/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow the use of type aliases | | | | | [`@typescript-eslint/no-unnecessary-condition`](./docs/rules/no-unnecessary-condition.md) | Prevents conditionals where the type is always truthy or always falsy | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Warns when a namespace qualifier is unnecessary | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Warns if an explicitly specified type argument is the default for that type parameter | | :wrench: | :thought_balloon: | +| [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Enforces that type arguments will not be used if not required | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | :heavy_check_mark: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-untyped-public-signature`](./docs/rules/no-untyped-public-signature.md) | Requires that all public method arguments and return type will be explicitly typed | | | | +| [`@typescript-eslint/no-untyped-public-signature`](./docs/rules/no-untyped-public-signature.md) | Disallow untyped public methods | | | | | [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | | | [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | | -| [`@typescript-eslint/no-unused-vars-experimental`](./docs/rules/no-unused-vars-experimental.md) | Disallow unused variables and arguments. | | | :thought_balloon: | +| [`@typescript-eslint/no-unused-vars-experimental`](./docs/rules/no-unused-vars-experimental.md) | Disallow unused variables and arguments | | | :thought_balloon: | | [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | :heavy_check_mark: | | | | [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | | [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements | :heavy_check_mark: | | | @@ -154,7 +155,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/prefer-nullish-coalescing`](./docs/rules/prefer-nullish-coalescing.md) | Enforce the usage of the nullish coalescing operator instead of logical chaining | | :wrench: | :thought_balloon: | | [`@typescript-eslint/prefer-optional-chain`](./docs/rules/prefer-optional-chain.md) | Prefer using concise optional chain expressions instead of chained logical ands | | :wrench: | | | [`@typescript-eslint/prefer-readonly`](./docs/rules/prefer-readonly.md) | Requires that private members are marked as `readonly` if they're never modified outside of the constructor | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Prefer RegExp#exec() over String#match() if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | :heavy_check_mark: | :wrench: | :thought_balloon: | | [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | | :thought_balloon: | | [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | @@ -162,9 +163,9 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | | | :thought_balloon: | | [`@typescript-eslint/restrict-template-expressions`](./docs/rules/restrict-template-expressions.md) | Enforce template literal expressions to be of string type | | | :thought_balloon: | -| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Rules for awaiting returned promises | | | :thought_balloon: | +| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | | :thought_balloon: | | [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | -| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | enforce consistent spacing before `function` definition opening parenthesis | | :wrench: | | +| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforces consistent spacing before function parenthesis | | :wrench: | | | [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: | | [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | :heavy_check_mark: | | | | [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | | diff --git a/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md b/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md index 01d37b218eba..e7a1596d1674 100644 --- a/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md +++ b/packages/eslint-plugin/docs/rules/adjacent-overload-signatures.md @@ -1,4 +1,4 @@ -# Require that member overloads be consecutive (adjacent-overload-signatures) +# Require that member overloads be consecutive (`adjacent-overload-signatures`) Grouping overloaded members together can improve readability of the code. diff --git a/packages/eslint-plugin/docs/rules/array-type.md b/packages/eslint-plugin/docs/rules/array-type.md index d76b7f8ea09c..2ed521c293a5 100644 --- a/packages/eslint-plugin/docs/rules/array-type.md +++ b/packages/eslint-plugin/docs/rules/array-type.md @@ -1,4 +1,4 @@ -# Requires using either `T[]` or `Array` for arrays (array-type) +# Requires using either `T[]` or `Array` for arrays (`array-type`) Using the same style for array definitions across your codebase makes it easier for your developers to read and understand the types. diff --git a/packages/eslint-plugin/docs/rules/await-thenable.md b/packages/eslint-plugin/docs/rules/await-thenable.md index 05eab6414914..fc65df9be38f 100644 --- a/packages/eslint-plugin/docs/rules/await-thenable.md +++ b/packages/eslint-plugin/docs/rules/await-thenable.md @@ -1,4 +1,4 @@ -# Disallows awaiting a value that is not a Promise (`await-thenable`) +# Disallows awaiting a value that is not a Thenable (`await-thenable`) This rule disallows awaiting a value that is not a "Thenable" (an object which has `then` method, such as a Promise). While it is valid JavaScript to await a non-`Promise`-like value (it will resolve immediately), this pattern is often a programmer error, such as forgetting to add parenthesis to call a function that returns a Promise. diff --git a/packages/eslint-plugin/docs/rules/ban-ts-ignore.md b/packages/eslint-plugin/docs/rules/ban-ts-ignore.md index 174ac5e78e4d..d8d987a95ed0 100644 --- a/packages/eslint-plugin/docs/rules/ban-ts-ignore.md +++ b/packages/eslint-plugin/docs/rules/ban-ts-ignore.md @@ -1,4 +1,4 @@ -# Bans “// @ts-ignore” comments from being used (ban-ts-ignore) +# Bans “// @ts-ignore” comments from being used (`ban-ts-ignore`) Suppressing TypeScript Compiler Errors can be hard to discover. diff --git a/packages/eslint-plugin/docs/rules/ban-types.md b/packages/eslint-plugin/docs/rules/ban-types.md index d3478062998e..d47643284488 100644 --- a/packages/eslint-plugin/docs/rules/ban-types.md +++ b/packages/eslint-plugin/docs/rules/ban-types.md @@ -1,4 +1,4 @@ -# Bans specific types from being used (ban-types) +# Bans specific types from being used (`ban-types`) This rule bans specific types and can suggest alternatives. It does not ban the corresponding runtime objects from being used. diff --git a/packages/eslint-plugin/docs/rules/brace-style.md b/packages/eslint-plugin/docs/rules/brace-style.md index 5a667294f2c8..c8268dea44ee 100644 --- a/packages/eslint-plugin/docs/rules/brace-style.md +++ b/packages/eslint-plugin/docs/rules/brace-style.md @@ -1,4 +1,4 @@ -# Enforce consistent brace style for blocks +# Enforce consistent brace style for blocks (`brace-style`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/class-name-casing.md b/packages/eslint-plugin/docs/rules/class-name-casing.md index 13b174548b3f..38c30609747a 100644 --- a/packages/eslint-plugin/docs/rules/class-name-casing.md +++ b/packages/eslint-plugin/docs/rules/class-name-casing.md @@ -1,4 +1,4 @@ -# Require PascalCase class and interface names (class-name-casing) +# Require PascalCased class and interface names (`class-name-casing`) This rule enforces PascalCase names for classes and interfaces. diff --git a/packages/eslint-plugin/docs/rules/consistent-type-assertions.md b/packages/eslint-plugin/docs/rules/consistent-type-assertions.md index c943f5a9ba14..550cdb711169 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-assertions.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-assertions.md @@ -1,4 +1,4 @@ -# Enforces consistent usage of type assertions. (consistent-type-assertions) +# Enforces consistent usage of type assertions (`consistent-type-assertions`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/consistent-type-definitions.md b/packages/eslint-plugin/docs/rules/consistent-type-definitions.md index 3e60f2ced7c2..1688552d8f0f 100644 --- a/packages/eslint-plugin/docs/rules/consistent-type-definitions.md +++ b/packages/eslint-plugin/docs/rules/consistent-type-definitions.md @@ -1,4 +1,4 @@ -# Consistent with type definition either `interface` or `type` (consistent-type-definitions) +# Consistent with type definition either `interface` or `type` (`consistent-type-definitions`) There are two ways to define a type. 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 27315771e428..360fdce01e65 100644 --- a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md +++ b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md @@ -1,4 +1,4 @@ -# Require explicit return types on functions and class methods (explicit-function-return-type) +# Require explicit return types on functions and class methods (`explicit-function-return-type`) Explicit types for function return values makes it clear to any calling code what type is returned. This ensures that the return value is assigned to a variable of the correct type; or in the case diff --git a/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md b/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md index a4cb8c3d5569..3a10696fa11e 100644 --- a/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md +++ b/packages/eslint-plugin/docs/rules/explicit-member-accessibility.md @@ -1,4 +1,4 @@ -# Require explicit accessibility modifiers on class properties and methods (explicit-member-accessibility) +# Require explicit accessibility modifiers on class properties and methods (`explicit-member-accessibility`) Leaving off accessibility modifier and making everything public can make your interface hard to use by others. diff --git a/packages/eslint-plugin/docs/rules/func-call-spacing.md b/packages/eslint-plugin/docs/rules/func-call-spacing.md index 9bcabde63dae..aa1d3fde96ad 100644 --- a/packages/eslint-plugin/docs/rules/func-call-spacing.md +++ b/packages/eslint-plugin/docs/rules/func-call-spacing.md @@ -1,4 +1,4 @@ -# require or disallow spacing between function identifiers and their invocations (`func-call-spacing`) +# Require or disallow spacing between function identifiers and their invocations (`func-call-spacing`) When calling a function, developers may insert optional whitespace between the function’s name and the parentheses that invoke it. This rule requires or disallows spaces between the function name and the opening parenthesis that calls it. diff --git a/packages/eslint-plugin/docs/rules/generic-type-naming.md b/packages/eslint-plugin/docs/rules/generic-type-naming.md index a24bb281d666..185b3811d9a8 100644 --- a/packages/eslint-plugin/docs/rules/generic-type-naming.md +++ b/packages/eslint-plugin/docs/rules/generic-type-naming.md @@ -1,4 +1,4 @@ -# Enforces naming of generic type variables (generic-type-naming) +# Enforces naming of generic type variables (`generic-type-naming`) It can be helpful to enforce a consistent naming style for generic type variables used within a type. For example, prefixing them with `T` and ensuring a somewhat descriptive name, or enforcing Hungarian notation. diff --git a/packages/eslint-plugin/docs/rules/indent.md b/packages/eslint-plugin/docs/rules/indent.md index 572b3e5fd1b5..4636cfeaeb56 100644 --- a/packages/eslint-plugin/docs/rules/indent.md +++ b/packages/eslint-plugin/docs/rules/indent.md @@ -1,4 +1,4 @@ -# Enforce consistent indentation (indent) +# Enforce consistent indentation (`indent`) There are several common guidelines which require specific indentation of nested blocks and statements, like: diff --git a/packages/eslint-plugin/docs/rules/interface-name-prefix.md b/packages/eslint-plugin/docs/rules/interface-name-prefix.md index 309dcbe4f243..f6439b490fd1 100644 --- a/packages/eslint-plugin/docs/rules/interface-name-prefix.md +++ b/packages/eslint-plugin/docs/rules/interface-name-prefix.md @@ -1,4 +1,4 @@ -# Require that interface names be prefixed with `I` (interface-name-prefix) +# Require that interface names should or should not prefixed with `I` (`interface-name-prefix`) Interfaces often represent important software contracts, so it can be helpful to prefix their names with `I`. The unprefixed name is then available for a class that provides a standard implementation of the interface. diff --git a/packages/eslint-plugin/docs/rules/member-delimiter-style.md b/packages/eslint-plugin/docs/rules/member-delimiter-style.md index 3504220fdcd5..ed1b09577563 100644 --- a/packages/eslint-plugin/docs/rules/member-delimiter-style.md +++ b/packages/eslint-plugin/docs/rules/member-delimiter-style.md @@ -1,4 +1,4 @@ -# Require a specific member delimiter style for interfaces and type literals (member-delimiter-style) +# Require a specific member delimiter style for interfaces and type literals (`member-delimiter-style`) Enforces a consistent member delimiter style in interfaces and type literals. There are three member delimiter styles primarily used in TypeScript: diff --git a/packages/eslint-plugin/docs/rules/member-naming.md b/packages/eslint-plugin/docs/rules/member-naming.md index 0d66a9f466c8..e12f4f239b56 100644 --- a/packages/eslint-plugin/docs/rules/member-naming.md +++ b/packages/eslint-plugin/docs/rules/member-naming.md @@ -1,4 +1,4 @@ -# Enforces naming conventions for class members by visibility. (member-naming) +# Enforces naming conventions for class members by visibility (`member-naming`) It can be helpful to enforce naming conventions for `private` (and sometimes `protected`) members of an object. For example, prefixing private properties with a `_` allows them to be easily discerned when being inspected by tools that do not have knowledge of TypeScript (such as most debuggers). diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md index d803e9def7e8..d028d844368a 100644 --- a/packages/eslint-plugin/docs/rules/member-ordering.md +++ b/packages/eslint-plugin/docs/rules/member-ordering.md @@ -1,4 +1,4 @@ -# Require a consistent member declaration order (member-ordering) +# Require a consistent member declaration order (`member-ordering`) A consistent ordering of fields, methods and constructors can make interfaces, type literals, classes and class expressions easier to read, navigate and edit. diff --git a/packages/eslint-plugin/docs/rules/no-array-constructor.md b/packages/eslint-plugin/docs/rules/no-array-constructor.md index 138f98acb13a..3ab59cbc6ffa 100644 --- a/packages/eslint-plugin/docs/rules/no-array-constructor.md +++ b/packages/eslint-plugin/docs/rules/no-array-constructor.md @@ -1,4 +1,4 @@ -# Disallow generic `Array` constructors (no-array-constructor) +# Disallow generic `Array` constructors (`no-array-constructor`) Use of the `Array` constructor to construct a new array is generally discouraged in favor of array literal notation because of the single-argument pitfall and because the `Array` global may be redefined. Two exceptions are when the Array constructor is used to intentionally create sparse arrays of a specified size by giving the constructor a single numeric argument, or when using TypeScript type parameters to specify the type of the items of the array (`new Array()`). diff --git a/packages/eslint-plugin/docs/rules/no-dynamic-delete.md b/packages/eslint-plugin/docs/rules/no-dynamic-delete.md index 76928c04764e..5d84cb1c9ade 100644 --- a/packages/eslint-plugin/docs/rules/no-dynamic-delete.md +++ b/packages/eslint-plugin/docs/rules/no-dynamic-delete.md @@ -1,4 +1,4 @@ -# Disallow the delete operator with computed key expressions (no-dynamic-delete) +# Disallow the delete operator with computed key expressions (`no-dynamic-delete`) Deleting dynamically computed keys can be dangerous and in some cases not well optimized. diff --git a/packages/eslint-plugin/docs/rules/no-empty-function.md b/packages/eslint-plugin/docs/rules/no-empty-function.md index 44f815c75d43..17628cb93c8b 100644 --- a/packages/eslint-plugin/docs/rules/no-empty-function.md +++ b/packages/eslint-plugin/docs/rules/no-empty-function.md @@ -1,4 +1,4 @@ -# Disallow Empty Functions (no-empty-function) +# Disallow empty functions (`no-empty-function`) Empty functions can reduce readability because readers need to guess whether it’s intentional or not. So writing a clear comment for empty functions is a good practice. diff --git a/packages/eslint-plugin/docs/rules/no-empty-interface.md b/packages/eslint-plugin/docs/rules/no-empty-interface.md index 37aa7e4dbc11..7df218a7eca8 100644 --- a/packages/eslint-plugin/docs/rules/no-empty-interface.md +++ b/packages/eslint-plugin/docs/rules/no-empty-interface.md @@ -1,4 +1,4 @@ -# Disallow the declaration of empty interfaces (no-empty-interface) +# Disallow the declaration of empty interfaces (`no-empty-interface`) An empty interface is equivalent to its supertype. If the interface does not implement a supertype, then the interface is equivalent to an empty object (`{}`). In both cases it can be omitted. diff --git a/packages/eslint-plugin/docs/rules/no-explicit-any.md b/packages/eslint-plugin/docs/rules/no-explicit-any.md index 55d42a0b1ddf..0c2713923492 100644 --- a/packages/eslint-plugin/docs/rules/no-explicit-any.md +++ b/packages/eslint-plugin/docs/rules/no-explicit-any.md @@ -1,4 +1,4 @@ -# Disallow usage of the `any` type (no-explicit-any) +# Disallow usage of the `any` type (`no-explicit-any`) Using the `any` type defeats the purpose of using TypeScript. When `any` is used, all compiler type checks around that value are ignored. diff --git a/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md b/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md index 37f4d2e3a52f..ced5d83ccb7e 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-extra-non-null-assertion.md @@ -1,4 +1,4 @@ -# Disallow extra non-null assertion +# Disallow extra non-null assertion (`no-extra-non-null-assertion`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/no-extra-parens.md b/packages/eslint-plugin/docs/rules/no-extra-parens.md index 9cd0cc48ab42..bcb21dec28df 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-parens.md +++ b/packages/eslint-plugin/docs/rules/no-extra-parens.md @@ -1,4 +1,4 @@ -# disallow unnecessary parentheses (`no-extra-parens`) +# Disallow unnecessary parentheses (`no-extra-parens`) This rule restricts the use of parentheses to only where they are necessary. diff --git a/packages/eslint-plugin/docs/rules/no-extra-semi.md b/packages/eslint-plugin/docs/rules/no-extra-semi.md index bb4597a93181..bed5e29d657b 100644 --- a/packages/eslint-plugin/docs/rules/no-extra-semi.md +++ b/packages/eslint-plugin/docs/rules/no-extra-semi.md @@ -1,4 +1,4 @@ -# Disallow unnecessary semicolons +# Disallow unnecessary semicolons (`no-extra-semi`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/no-extraneous-class.md b/packages/eslint-plugin/docs/rules/no-extraneous-class.md index 3c1fa0528351..b387d67a9933 100644 --- a/packages/eslint-plugin/docs/rules/no-extraneous-class.md +++ b/packages/eslint-plugin/docs/rules/no-extraneous-class.md @@ -1,4 +1,4 @@ -# Forbids the use of classes as namespaces (no-extraneous-class) +# Forbids the use of classes as namespaces (`no-extraneous-class`) This rule warns when a class is accidentally used as a namespace. diff --git a/packages/eslint-plugin/docs/rules/no-floating-promises.md b/packages/eslint-plugin/docs/rules/no-floating-promises.md index 64e6accb45de..c3f8f8c77a86 100644 --- a/packages/eslint-plugin/docs/rules/no-floating-promises.md +++ b/packages/eslint-plugin/docs/rules/no-floating-promises.md @@ -1,4 +1,4 @@ -# Requires Promise-like values to be handled appropriately (no-floating-promises) +# Requires Promise-like values to be handled appropriately (`no-floating-promises`) This rule forbids usage of Promise-like values in statements without handling their errors appropriately. Unhandled promises can cause several issues, such diff --git a/packages/eslint-plugin/docs/rules/no-for-in-array.md b/packages/eslint-plugin/docs/rules/no-for-in-array.md index 3a1cf2bdd370..c5d4c3f648dd 100644 --- a/packages/eslint-plugin/docs/rules/no-for-in-array.md +++ b/packages/eslint-plugin/docs/rules/no-for-in-array.md @@ -1,4 +1,4 @@ -# Disallow iterating over an array with a for-in loop (no-for-in-array) +# Disallow iterating over an array with a for-in loop (`no-for-in-array`) This rule prohibits iterating over an array with a for-in loop. diff --git a/packages/eslint-plugin/docs/rules/no-implied-eval.md b/packages/eslint-plugin/docs/rules/no-implied-eval.md new file mode 100644 index 000000000000..a16844a6d43d --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-implied-eval.md @@ -0,0 +1,95 @@ +# Disallow the use of `eval()`-like methods (`no-implied-eval`) + +It's considered a good practice to avoid using `eval()`. There are security and performance implications involved with doing so, which is why many linters recommend disallowing `eval()`. However, there are some other ways to pass a string and have it interpreted as JavaScript code that have similar concerns. + +The first is using `setTimeout()`, `setInterval()`, `setImmediate` or `execScript()` (Internet Explorer only), all of which can accept a string of code as their first argument + +```ts +setTimeout('alert(`Hi!`);', 100); +``` + +or using `new Function()` + +```ts +const fn = new Function('a', 'b', 'return a + b'); +``` + +This is considered an implied `eval()` because a string of code is +passed in to be interpreted. The same can be done with `setInterval()`, `setImmediate()` and `execScript()`. All interpret the JavaScript code in the global scope. + +The best practice is to avoid using `new Function()` or `execScript()` and always use a function for the first argument of `setTimeout()`, `setInterval()` and `setImmediate()`. + +## Rule Details + +This rule aims to eliminate implied `eval()` through the use of `new Function()`, `setTimeout()`, `setInterval()`, `setImmediate()` or `execScript()`. + +Examples of **incorrect** code for this rule: + +```ts +/* eslint @typescript-eslint/no-implied-eval: "error" */ + +setTimeout('alert(`Hi!`);', 100); + +setInterval('alert(`Hi!`);', 100); + +setImmediate('alert(`Hi!`)'); + +execScript('alert(`Hi!`)'); + +window.setTimeout('count = 5', 10); + +window.setInterval('foo = bar', 10); + +const fn = '() = {}'; +setTimeout(fn, 100); + +const fn = () => { + return 'x = 10'; +}; +setTimeout(fn(), 100); + +const fn = new Function('a', 'b', 'return a + b'); +``` + +Examples of **correct** code for this rule: + +```ts +/* eslint @typescript-eslint/no-implied-eval: "error" */ + +setTimeout(function() { + alert('Hi!'); +}, 100); + +setInterval(function() { + alert('Hi!'); +}, 100); + +setImmediate(function() { + alert('Hi!'); +}); + +execScript(function() { + alert('Hi!'); +}); + +const fn = () => {}; +setTimeout(fn, 100); + +const foo = { + fn: function() {}, +}; +setTimeout(foo.fn, 100); +setTimeout(foo.fn.bind(this), 100); + +class Foo { + static fn = () => {}; +} + +setTimeout(Foo.fn, 100); +``` + +## When Not To Use It + +If you want to allow `new Function()` or `setTimeout()`, `setInterval()`, `setImmediate()` and `execScript()` with string arguments, then you can safely disable this rule. + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-implied-eval.md) diff --git a/packages/eslint-plugin/docs/rules/no-inferrable-types.md b/packages/eslint-plugin/docs/rules/no-inferrable-types.md index 7dd159b02e5a..91fdfe6784a4 100644 --- a/packages/eslint-plugin/docs/rules/no-inferrable-types.md +++ b/packages/eslint-plugin/docs/rules/no-inferrable-types.md @@ -1,4 +1,4 @@ -# Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean. (no-inferrable-types) +# Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean (`no-inferrable-types`) Explicit types where they can be easily inferred may add unnecessary verbosity. diff --git a/packages/eslint-plugin/docs/rules/no-magic-numbers.md b/packages/eslint-plugin/docs/rules/no-magic-numbers.md index c7903cd0bea9..377e92723607 100644 --- a/packages/eslint-plugin/docs/rules/no-magic-numbers.md +++ b/packages/eslint-plugin/docs/rules/no-magic-numbers.md @@ -1,4 +1,4 @@ -# Disallow Magic Numbers (`no-magic-numbers`) +# Disallow magic numbers (`no-magic-numbers`) 'Magic numbers' are numbers that occur multiple times in code without an explicit meaning. They should preferably be replaced by named constants. diff --git a/packages/eslint-plugin/docs/rules/no-misused-new.md b/packages/eslint-plugin/docs/rules/no-misused-new.md index df1aec7ad605..f315fcac289c 100644 --- a/packages/eslint-plugin/docs/rules/no-misused-new.md +++ b/packages/eslint-plugin/docs/rules/no-misused-new.md @@ -1,4 +1,4 @@ -# Enforce valid definition of `new` and `constructor`. (no-misused-new) +# Enforce valid definition of `new` and `constructor` (`no-misused-new`) Warns on apparent attempts to define constructors for interfaces or `new` for classes. diff --git a/packages/eslint-plugin/docs/rules/no-misused-promises.md b/packages/eslint-plugin/docs/rules/no-misused-promises.md index ffca96632a7e..6a4c2a557921 100644 --- a/packages/eslint-plugin/docs/rules/no-misused-promises.md +++ b/packages/eslint-plugin/docs/rules/no-misused-promises.md @@ -1,4 +1,4 @@ -# Avoid using promises in places not designed to handle them (no-misused-promises) +# Avoid using promises in places not designed to handle them (`no-misused-promises`) This rule forbids using promises in places where the TypeScript compiler allows them but they are not handled properly. These situations can often arise diff --git a/packages/eslint-plugin/docs/rules/no-namespace.md b/packages/eslint-plugin/docs/rules/no-namespace.md index 86ddeeb1e49b..3bcb1a3868ad 100644 --- a/packages/eslint-plugin/docs/rules/no-namespace.md +++ b/packages/eslint-plugin/docs/rules/no-namespace.md @@ -1,4 +1,4 @@ -# Disallow the use of custom TypeScript modules and namespaces (no-namespace) +# Disallow the use of custom TypeScript modules and namespaces (`no-namespace`) Custom TypeScript modules (`module foo {}`) and namespaces (`namespace foo {}`) are considered outdated ways to organize TypeScript code. ES2015 module syntax is now preferred (`import`/`export`). diff --git a/packages/eslint-plugin/docs/rules/no-non-null-assertion.md b/packages/eslint-plugin/docs/rules/no-non-null-assertion.md index 0a7c2a1a8e81..5241a43e23fe 100644 --- a/packages/eslint-plugin/docs/rules/no-non-null-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-non-null-assertion.md @@ -1,4 +1,4 @@ -# Disallows non-null assertions using the `!` postfix operator (no-non-null-assertion) +# Disallows non-null assertions using the `!` postfix operator (`no-non-null-assertion`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/no-parameter-properties.md b/packages/eslint-plugin/docs/rules/no-parameter-properties.md index a7f28f116f2f..86ebed8bc7fd 100644 --- a/packages/eslint-plugin/docs/rules/no-parameter-properties.md +++ b/packages/eslint-plugin/docs/rules/no-parameter-properties.md @@ -1,4 +1,4 @@ -# Disallow the use of parameter properties in class constructors. (no-parameter-properties) +# Disallow the use of parameter properties in class constructors (`no-parameter-properties`) Parameter properties can be confusing to those new to TypeScript as they are less explicit than other ways of declaring and initializing class members. diff --git a/packages/eslint-plugin/docs/rules/no-require-imports.md b/packages/eslint-plugin/docs/rules/no-require-imports.md index 0d2b831a1acd..655fc57bd60a 100644 --- a/packages/eslint-plugin/docs/rules/no-require-imports.md +++ b/packages/eslint-plugin/docs/rules/no-require-imports.md @@ -1,4 +1,4 @@ -# Disallows invocation of `require()` (no-require-imports) +# Disallows invocation of `require()` (`no-require-imports`) Prefer the newer ES6-style imports over `require()`. diff --git a/packages/eslint-plugin/docs/rules/no-this-alias.md b/packages/eslint-plugin/docs/rules/no-this-alias.md index 4a512be31eb8..7f5ee719fa0f 100644 --- a/packages/eslint-plugin/docs/rules/no-this-alias.md +++ b/packages/eslint-plugin/docs/rules/no-this-alias.md @@ -1,4 +1,4 @@ -# Disallow aliasing `this` (no-this-alias) +# Disallow aliasing `this` (`no-this-alias`) This rule prohibits assigning variables to `this`. diff --git a/packages/eslint-plugin/docs/rules/no-throw-literal.md b/packages/eslint-plugin/docs/rules/no-throw-literal.md index 9f87cffb34f8..3b7ee9c2d977 100644 --- a/packages/eslint-plugin/docs/rules/no-throw-literal.md +++ b/packages/eslint-plugin/docs/rules/no-throw-literal.md @@ -1,4 +1,4 @@ -# Restrict what can be thrown as an exception (`@typescript-eslint/no-throw-literal`) +# Disallow throwing literals as exceptions (`no-throw-literal`) It is considered good practice to only `throw` the `Error` object itself or an object using the `Error` object as base objects for user-defined exceptions. The fundamental benefit of `Error` objects is that they automatically keep track of where they were built and originated. diff --git a/packages/eslint-plugin/docs/rules/no-type-alias.md b/packages/eslint-plugin/docs/rules/no-type-alias.md index c1f2cd9934b8..9d7f1d47744a 100644 --- a/packages/eslint-plugin/docs/rules/no-type-alias.md +++ b/packages/eslint-plugin/docs/rules/no-type-alias.md @@ -1,4 +1,4 @@ -# Disallow the use of type aliases (no-type-alias) +# Disallow the use of type aliases (`no-type-alias`) In TypeScript, type aliases serve three purposes: diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md b/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md index cf6c277dfb5e..ae1e8eb0aa04 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-condition.md @@ -1,4 +1,4 @@ -# Condition expressions must be necessary +# Prevents conditionals where the type is always truthy or always falsy (`no-unnecessary-condition`) Any expression being used as a condition must be able to evaluate as truthy or falsy in order to be considered "necessary". Conversely, any expression that always evaluates to truthy or always evaluates to falsy, as determined by the type of the expression, is considered unnecessary and will be flagged by this rule. diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md b/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md index 82d6cce179ba..56aba181cbae 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-qualifier.md @@ -1,4 +1,4 @@ -# Warns when a namespace qualifier is unnecessary. (no-unnecessary-qualifier) +# Warns when a namespace qualifier is unnecessary (`no-unnecessary-qualifier`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md index 90fc10a83850..f5caaa42cdfa 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md @@ -1,4 +1,4 @@ -# Enforces that type arguments will not be used if not required (no-unnecessary-type-arguments) +# Enforces that type arguments will not be used if not required (`no-unnecessary-type-arguments`) Warns if an explicitly specified type argument is the default for that type parameter. diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md index d1275c6d5977..14819c56bf78 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md @@ -1,4 +1,4 @@ -# Warns if a type assertion does not change the type of an expression (no-unnecessary-type-assertion) +# Warns if a type assertion does not change the type of an expression (`no-unnecessary-type-assertion`) This rule prohibits using a type assertion that does not change the type of an expression. diff --git a/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md b/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md index 0c26412d4a1d..87a17895c384 100644 --- a/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md +++ b/packages/eslint-plugin/docs/rules/no-untyped-public-signature.md @@ -1,4 +1,4 @@ -# Disallow untyped public methods (no-untyped-public-signature) +# Disallow untyped public methods (`no-untyped-public-signature`) public methods are meant to be used by code outside of your class. By typing both the parameters and the return type of public methods they will be more readable and easy to use. diff --git a/packages/eslint-plugin/docs/rules/no-unused-expressions.md b/packages/eslint-plugin/docs/rules/no-unused-expressions.md index 797a5269991b..63674120fbcb 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-expressions.md +++ b/packages/eslint-plugin/docs/rules/no-unused-expressions.md @@ -1,4 +1,4 @@ -# Disallow Unused Expressions (no-unused-expressions) +# Disallow unused expressions (`no-unused-expressions`) This rule aims to eliminate unused expressions which have no effect on the state of the program. diff --git a/packages/eslint-plugin/docs/rules/no-unused-vars-experimental.md b/packages/eslint-plugin/docs/rules/no-unused-vars-experimental.md index bf2f245fe139..918cbc595f5d 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-vars-experimental.md +++ b/packages/eslint-plugin/docs/rules/no-unused-vars-experimental.md @@ -1,4 +1,4 @@ -# Disallow unused variables and arguments (no-unused-vars-experimental) +# Disallow unused variables and arguments (`no-unused-vars-experimental`) Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers. diff --git a/packages/eslint-plugin/docs/rules/no-unused-vars.md b/packages/eslint-plugin/docs/rules/no-unused-vars.md index 7c7d13966c55..a84b4362d61e 100644 --- a/packages/eslint-plugin/docs/rules/no-unused-vars.md +++ b/packages/eslint-plugin/docs/rules/no-unused-vars.md @@ -1,4 +1,4 @@ -# Disallow unused variables (no-unused-vars) +# Disallow unused variables (`no-unused-vars`) Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers. diff --git a/packages/eslint-plugin/docs/rules/no-use-before-define.md b/packages/eslint-plugin/docs/rules/no-use-before-define.md index 64e7ee73230a..e6acf6045683 100644 --- a/packages/eslint-plugin/docs/rules/no-use-before-define.md +++ b/packages/eslint-plugin/docs/rules/no-use-before-define.md @@ -1,4 +1,4 @@ -# Disallow the use of variables before they are defined (no-use-before-define) +# Disallow the use of variables before they are defined (`no-use-before-define`) In JavaScript, prior to ES6, variable and function declarations are hoisted to the top of a scope, so it's possible to use identifiers before their formal declarations in code. This can be confusing and some believe it is best to always declare variables and functions before using them. diff --git a/packages/eslint-plugin/docs/rules/no-useless-constructor.md b/packages/eslint-plugin/docs/rules/no-useless-constructor.md index 0aa1f4a5c984..757ae5d4a127 100644 --- a/packages/eslint-plugin/docs/rules/no-useless-constructor.md +++ b/packages/eslint-plugin/docs/rules/no-useless-constructor.md @@ -1,4 +1,4 @@ -# Disallow unnecessary constructors (no-useless-constructor) +# Disallow unnecessary constructors (`no-useless-constructor`) ES2015 provides a default class constructor if one is not specified. As such, it is unnecessary to provide an empty constructor or one that simply delegates into its parent class, as in the following examples: diff --git a/packages/eslint-plugin/docs/rules/no-var-requires.md b/packages/eslint-plugin/docs/rules/no-var-requires.md index fb8e7a547216..104a54943cda 100644 --- a/packages/eslint-plugin/docs/rules/no-var-requires.md +++ b/packages/eslint-plugin/docs/rules/no-var-requires.md @@ -1,4 +1,4 @@ -# Disallows the use of require statements except in import statements (no-var-requires) +# Disallows the use of require statements except in import statements (`no-var-requires`) In other words, the use of forms such as `var foo = require("foo")` are banned. Instead use ES6 style imports or `import foo = require("foo")` imports. diff --git a/packages/eslint-plugin/docs/rules/prefer-for-of.md b/packages/eslint-plugin/docs/rules/prefer-for-of.md index a937709f7ebe..0b1e5570d757 100644 --- a/packages/eslint-plugin/docs/rules/prefer-for-of.md +++ b/packages/eslint-plugin/docs/rules/prefer-for-of.md @@ -1,4 +1,4 @@ -# Use for-of loops instead of standard for loops over arrays (prefer-for-of) +# Prefer a ‘for-of’ loop over a standard ‘for’ loop if the index is only used to access the array being iterated (`prefer-for-of`) This rule recommends a for-of loop when the loop index is only used to read from an array that is being iterated. diff --git a/packages/eslint-plugin/docs/rules/prefer-function-type.md b/packages/eslint-plugin/docs/rules/prefer-function-type.md index 7cbf2be0480d..a3210a133a6e 100644 --- a/packages/eslint-plugin/docs/rules/prefer-function-type.md +++ b/packages/eslint-plugin/docs/rules/prefer-function-type.md @@ -1,4 +1,4 @@ -# Use function types instead of interfaces with call signatures (prefer-function-type) +# Use function types instead of interfaces with call signatures (`prefer-function-type`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/prefer-includes.md b/packages/eslint-plugin/docs/rules/prefer-includes.md index e93f248d2b65..07cc9ea44e12 100644 --- a/packages/eslint-plugin/docs/rules/prefer-includes.md +++ b/packages/eslint-plugin/docs/rules/prefer-includes.md @@ -1,4 +1,4 @@ -# Enforce `includes` method over `indexOf` method (prefer-includes) +# Enforce `includes` method over `indexOf` method (`prefer-includes`) Until ES5, we were using `String#indexOf` method to check whether a string contains an arbitrary substring or not. Until ES2015, we were using `Array#indexOf` method to check whether an array contains an arbitrary value or not. diff --git a/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md b/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md index 95d25f72d9ca..faeeaadf8c58 100644 --- a/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md +++ b/packages/eslint-plugin/docs/rules/prefer-namespace-keyword.md @@ -1,4 +1,4 @@ -# Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules. (prefer-namespace-keyword) +# Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules (`prefer-namespace-keyword`) In an effort to prevent further confusion between custom TypeScript modules and the new ES2015 modules, starting with TypeScript `v1.5` the keyword `namespace` is now the preferred way to declare custom TypeScript modules. diff --git a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md index ef65adbca2ea..5b99e5c613f6 100644 --- a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md +++ b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md @@ -1,4 +1,4 @@ -# Enforce the usage of the nullish coalescing operator instead of logical chaining (prefer-nullish-coalescing) +# Enforce the usage of the nullish coalescing operator instead of logical chaining (`prefer-nullish-coalescing`) TypeScript 3.7 added support for the nullish coalescing operator. This operator allows you to safely cascade a value when dealing with `null` or `undefined`. diff --git a/packages/eslint-plugin/docs/rules/prefer-optional-chain.md b/packages/eslint-plugin/docs/rules/prefer-optional-chain.md index 410db3c931aa..9a046b3d82f8 100644 --- a/packages/eslint-plugin/docs/rules/prefer-optional-chain.md +++ b/packages/eslint-plugin/docs/rules/prefer-optional-chain.md @@ -1,4 +1,4 @@ -# Prefer using concise optional chain expressions instead of chained logical ands (prefer-optional-chain) +# Prefer using concise optional chain expressions instead of chained logical ands (`prefer-optional-chain`) TypeScript 3.7 added support for the optional chain operator. This operator allows you to safely access properties and methods on objects when they are potentially `null` or `undefined`. diff --git a/packages/eslint-plugin/docs/rules/prefer-readonly.md b/packages/eslint-plugin/docs/rules/prefer-readonly.md index 8f238b1adfd2..6731a7585807 100644 --- a/packages/eslint-plugin/docs/rules/prefer-readonly.md +++ b/packages/eslint-plugin/docs/rules/prefer-readonly.md @@ -1,4 +1,4 @@ -# require never-modified private members be marked as `readonly` (prefer-readonly) +# Requires that private members are marked as `readonly` if they're never modified outside of the constructor (`prefer-readonly`) This rule enforces that private members are marked as `readonly` if they're never modified outside of the constructor. diff --git a/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md b/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md index 9e55a09595cb..4640d35a98ec 100644 --- a/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md +++ b/packages/eslint-plugin/docs/rules/prefer-regexp-exec.md @@ -1,4 +1,4 @@ -# Enforce to use `RegExp#exec` over `String#match` (prefer-regexp-exec) +# Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided (`prefer-regexp-exec`) `RegExp#exec` is faster than `String#match` and both work the same when not using the `/g` flag. diff --git a/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md b/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md index 07ad4e1148fb..110061d554b7 100644 --- a/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md +++ b/packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md @@ -1,4 +1,4 @@ -# Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings (prefer-string-starts-ends-with) +# Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings (`prefer-string-starts-ends-with`) There are multiple ways to verify if a string starts or ends with a specific string, such as `foo.indexOf('bar') === 0`. diff --git a/packages/eslint-plugin/docs/rules/promise-function-async.md b/packages/eslint-plugin/docs/rules/promise-function-async.md index 79000974cead..192e66a35775 100644 --- a/packages/eslint-plugin/docs/rules/promise-function-async.md +++ b/packages/eslint-plugin/docs/rules/promise-function-async.md @@ -1,4 +1,4 @@ -# Functions that return promises must be async (promise-function-async) +# Requires any function or method that returns a Promise to be marked async (`promise-function-async`) Requires any function or method that returns a Promise to be marked async. Ensures that each function is only capable of: diff --git a/packages/eslint-plugin/docs/rules/quotes.md b/packages/eslint-plugin/docs/rules/quotes.md index 0a484e3fae7d..e9c37df36f75 100644 --- a/packages/eslint-plugin/docs/rules/quotes.md +++ b/packages/eslint-plugin/docs/rules/quotes.md @@ -1,4 +1,4 @@ -# Enforce the consistent use of either backticks, double, or single quotes +# Enforce the consistent use of either backticks, double, or single quotes (`quotes`) ## Rule Details diff --git a/packages/eslint-plugin/docs/rules/require-array-sort-compare.md b/packages/eslint-plugin/docs/rules/require-array-sort-compare.md index 4b56f999867b..5b30533a9e9f 100644 --- a/packages/eslint-plugin/docs/rules/require-array-sort-compare.md +++ b/packages/eslint-plugin/docs/rules/require-array-sort-compare.md @@ -1,4 +1,4 @@ -# Enforce giving `compare` argument to `Array#sort` (require-array-sort-compare) +# Enforce giving `compare` argument to `Array#sort` (`require-array-sort-compare`) This rule prevents to invoke `Array#sort()` method without `compare` argument. diff --git a/packages/eslint-plugin/docs/rules/require-await.md b/packages/eslint-plugin/docs/rules/require-await.md index 1b7b819154d5..23160dc7db5d 100644 --- a/packages/eslint-plugin/docs/rules/require-await.md +++ b/packages/eslint-plugin/docs/rules/require-await.md @@ -1,4 +1,4 @@ -# Disallow async functions which have no await expression (require-await) +# Disallow async functions which have no `await` expression (`require-await`) Asynchronous functions that don’t use `await` might not need to be asynchronous functions and could be the unintentional result of refactoring. diff --git a/packages/eslint-plugin/docs/rules/restrict-plus-operands.md b/packages/eslint-plugin/docs/rules/restrict-plus-operands.md index 179ededd25b9..0959f837739b 100644 --- a/packages/eslint-plugin/docs/rules/restrict-plus-operands.md +++ b/packages/eslint-plugin/docs/rules/restrict-plus-operands.md @@ -1,4 +1,4 @@ -# When adding two variables, operands must both be of type number or of type string. (restrict-plus-operands) +# When adding two variables, operands must both be of type number or of type string (`restrict-plus-operands`) Examples of **correct** code: diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md index 0cb1dd63e0bc..549da98eebd3 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.md +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.md @@ -1,4 +1,4 @@ -# Enforce template literal expressions to be of string type. (restrict-template-expressions) +# Enforce template literal expressions to be of string type (`restrict-template-expressions`) Examples of **correct** code: diff --git a/packages/eslint-plugin/docs/rules/return-await.md b/packages/eslint-plugin/docs/rules/return-await.md index eb7c41ef8fdb..c00c2be5d02f 100644 --- a/packages/eslint-plugin/docs/rules/return-await.md +++ b/packages/eslint-plugin/docs/rules/return-await.md @@ -1,4 +1,4 @@ -# Require/Disallow returning awaited values in specific contexts (return-await) +# Enforces consistent returning of awaited values (`return-await`) Returning an awaited promise can make sense for better stack trace information as well as for consistent error handling (returned promises will not be caught in an async function try/catch). diff --git a/packages/eslint-plugin/docs/rules/semi.md b/packages/eslint-plugin/docs/rules/semi.md index 2a93d22a3135..bbb3154fa1a8 100644 --- a/packages/eslint-plugin/docs/rules/semi.md +++ b/packages/eslint-plugin/docs/rules/semi.md @@ -1,4 +1,4 @@ -# require or disallow semicolons instead of ASI (semi) +# Require or disallow semicolons instead of ASI (`semi`) This rule enforces consistent use of semicolons after statements. diff --git a/packages/eslint-plugin/docs/rules/space-before-function-paren.md b/packages/eslint-plugin/docs/rules/space-before-function-paren.md index a25ea5002600..bfbcb22d8871 100644 --- a/packages/eslint-plugin/docs/rules/space-before-function-paren.md +++ b/packages/eslint-plugin/docs/rules/space-before-function-paren.md @@ -1,4 +1,4 @@ -# Require or disallow a space before function parenthesis (`space-before-function-paren`) +# Enforces consistent spacing before function parenthesis (`space-before-function-paren`) When formatting a function, whitespace is allowed between the function name or `function` keyword and the opening parenthesis. Named functions also require a space between the `function` keyword and the function name, but anonymous functions require no whitespace. For example: diff --git a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md index fd23d75cb5ef..cec621588ef2 100644 --- a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md +++ b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md @@ -1,4 +1,4 @@ -# Boolean expressions are limited to booleans (strict-boolean-expressions) +# Restricts the types allowed in boolean expressions (`strict-boolean-expressions`) Requires that any boolean expression is limited to true booleans rather than casting another primitive to a boolean at runtime. @@ -57,6 +57,7 @@ while (typeof str !== 'undefined') { Options may be provided as an object with: - `allowNullable` to allow `undefined` and `null` in addition to `boolean` as a type of all boolean expressions. (`false` by default). +- `allowSafe` to allow non-falsy types (i.e. non string / number / boolean) in addition to `boolean` as a type of all boolean expressions. (`false` by default). - `ignoreRhs` to skip the check on the right hand side of expressions like `a && b` or `a || b` - allows these operators to be used for their short-circuiting behavior. (`false` by default). ## Related To diff --git a/packages/eslint-plugin/docs/rules/triple-slash-reference.md b/packages/eslint-plugin/docs/rules/triple-slash-reference.md index 7f9324a417fe..af9e5498b6d4 100644 --- a/packages/eslint-plugin/docs/rules/triple-slash-reference.md +++ b/packages/eslint-plugin/docs/rules/triple-slash-reference.md @@ -1,4 +1,4 @@ -# Sets preference level for triple slash directives versus ES6-style import declarations. (triple-slash-reference) +# Sets preference level for triple slash directives versus ES6-style import declarations (`triple-slash-reference`) Use of triple-slash reference type directives is discouraged in favor of the newer `import` style. This rule allows you to ban use of `/// `, `/// `, or `/// ` directives. diff --git a/packages/eslint-plugin/docs/rules/type-annotation-spacing.md b/packages/eslint-plugin/docs/rules/type-annotation-spacing.md index 68075c277680..c3a889cf36fd 100644 --- a/packages/eslint-plugin/docs/rules/type-annotation-spacing.md +++ b/packages/eslint-plugin/docs/rules/type-annotation-spacing.md @@ -1,4 +1,4 @@ -# Require consistent spacing around type annotations (type-annotation-spacing) +# Require consistent spacing around type annotations (`type-annotation-spacing`) Spacing around type annotations improves readability of the code. Although the most commonly used style guideline for type annotations in TypeScript prescribes adding a space after the colon, but not before it, it is subjective to the preferences of a project. For example: diff --git a/packages/eslint-plugin/docs/rules/typedef.md b/packages/eslint-plugin/docs/rules/typedef.md index 38d25ac1d6a5..eac8472d9462 100644 --- a/packages/eslint-plugin/docs/rules/typedef.md +++ b/packages/eslint-plugin/docs/rules/typedef.md @@ -1,4 +1,4 @@ -# Require type annotations to exist (`typedef`) +# Requires type annotations to exist (`typedef`) TypeScript cannot always infer types for all places in code. Some locations require type annotations for their types to be inferred. diff --git a/packages/eslint-plugin/docs/rules/unbound-method.md b/packages/eslint-plugin/docs/rules/unbound-method.md index d0268dadeec3..3903e133b429 100644 --- a/packages/eslint-plugin/docs/rules/unbound-method.md +++ b/packages/eslint-plugin/docs/rules/unbound-method.md @@ -1,4 +1,4 @@ -# Enforces unbound methods are called with their expected scope (unbound-method) +# Enforces unbound methods are called with their expected scope (`unbound-method`) Warns when a method is used outside of a method call. diff --git a/packages/eslint-plugin/docs/rules/unified-signatures.md b/packages/eslint-plugin/docs/rules/unified-signatures.md index 11f79b10c5b7..7ed5116c2aa6 100644 --- a/packages/eslint-plugin/docs/rules/unified-signatures.md +++ b/packages/eslint-plugin/docs/rules/unified-signatures.md @@ -1,4 +1,4 @@ -# Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter. (unified-signatures) +# Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter (`unified-signatures`) Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter. diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 449adb762db6..572d0e3101ca 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "2.14.0", + "version": "2.15.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -35,12 +35,12 @@ "clean": "tsc -b tsconfig.build.json --clean", "format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore", "generate:configs": "../../node_modules/.bin/ts-node --files --transpile-only tools/generate-configs.ts", - "lint": "eslint . --ext .js,.ts --ignore-path='../../.eslintignore'", + "lint": "eslint . --ext .js,.ts --ignore-path ../../.eslintignore", "test": "jest --coverage", "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.14.0", + "@typescript-eslint/experimental-utils": "2.15.0", "eslint-utils": "^1.4.3", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index b350f86de392..f9eed1fca9cf 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -39,6 +39,7 @@ "@typescript-eslint/no-extraneous-class": "error", "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-implied-eval": "error", "@typescript-eslint/no-inferrable-types": "error", "no-magic-numbers": "off", "@typescript-eslint/no-magic-numbers": "error", diff --git a/packages/eslint-plugin/src/rules/class-name-casing.ts b/packages/eslint-plugin/src/rules/class-name-casing.ts index b27bf1ebcf14..cbcc805630d4 100644 --- a/packages/eslint-plugin/src/rules/class-name-casing.ts +++ b/packages/eslint-plugin/src/rules/class-name-casing.ts @@ -67,7 +67,13 @@ export default util.createRule({ * @param decl The declaration * @param id The name of the declaration */ - function report(decl: TSESTree.Node, id: TSESTree.Identifier): void { + function report( + decl: + | TSESTree.ClassDeclaration + | TSESTree.TSInterfaceDeclaration + | TSESTree.ClassExpression, + id: TSESTree.Identifier, + ): void { let friendlyName; switch (decl.type) { @@ -78,8 +84,6 @@ export default util.createRule({ case AST_NODE_TYPES.TSInterfaceDeclaration: friendlyName = 'Interface'; break; - default: - friendlyName = decl.type; } context.report({ diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 2206ca0a087d..ae54149d0a74 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -27,7 +27,7 @@ export default util.createRule({ type: 'suggestion', docs: { category: 'Best Practices', - description: 'Enforces consistent usage of type assertions.', + description: 'Enforces consistent usage of type assertions', recommended: 'error', }, messages: { diff --git a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts index fe8c38c03413..ebfbae27329d 100644 --- a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts +++ b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts @@ -183,7 +183,7 @@ export default util.createRule({ /** * Checks that the parameter property has the desired accessibility modifiers set. - * @param {TSESTree.TSParameterProperty} node The node representing a Parameter Property + * @param node The node representing a Parameter Property */ function checkParameterPropertyAccessibilityModifier( node: TSESTree.TSParameterProperty, diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index a14884bbbd89..5d2f53cce251 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -28,6 +28,7 @@ import noExtraSemi from './no-extra-semi'; import noExtraneousClass from './no-extraneous-class'; 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 noMagicNumbers from './no-magic-numbers'; import noMisusedNew from './no-misused-new'; @@ -107,6 +108,7 @@ export default { 'no-floating-promises': noFloatingPromises, 'no-for-in-array': noForInArray, 'no-inferrable-types': noInferrableTypes, + 'no-implied-eval': noImpliedEval, 'no-magic-numbers': noMagicNumbers, 'no-misused-new': noMisusedNew, 'no-misused-promises': noMisusedPromises, diff --git a/packages/eslint-plugin/src/rules/no-array-constructor.ts b/packages/eslint-plugin/src/rules/no-array-constructor.ts index 749ce2ba7ada..731cb960973c 100644 --- a/packages/eslint-plugin/src/rules/no-array-constructor.ts +++ b/packages/eslint-plugin/src/rules/no-array-constructor.ts @@ -15,7 +15,7 @@ export default util.createRule({ }, fixable: 'code', messages: { - useLiteral: 'The array literal notation [] is preferrable.', + useLiteral: 'The array literal notation [] is preferable.', }, schema: [], }, diff --git a/packages/eslint-plugin/src/rules/no-dynamic-delete.ts b/packages/eslint-plugin/src/rules/no-dynamic-delete.ts index 6fbdba1058fc..71ce7f2d91e8 100644 --- a/packages/eslint-plugin/src/rules/no-dynamic-delete.ts +++ b/packages/eslint-plugin/src/rules/no-dynamic-delete.ts @@ -11,8 +11,7 @@ export default util.createRule({ meta: { docs: { category: 'Best Practices', - description: - 'Bans usage of the delete operator with computed key expressions', + description: 'Disallow the delete operator with computed key expressions', recommended: false, }, fixable: 'code', diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index cb43721cac45..039415ad68f4 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -49,33 +49,25 @@ export default util.createRule({ return; } - if (!node.extends || node.extends.length === 0) { + const extend = node.extends; + if (!extend || extend.length === 0) { context.report({ node: node.id, messageId: 'noEmpty', }); - } else if (node.extends.length === 1) { + } else if (extend.length === 1) { // interface extends exactly 1 interface --> Report depending on rule setting - if (allowSingleExtends) { - return; - } else { + if (!allowSingleExtends) { context.report({ node: node.id, messageId: 'noEmptyWithSuper', - fix(fixer) { - if (node.extends && node.extends.length) { - return [ - fixer.replaceText( - node, - `type ${sourceCode.getText( - node.id, - )} = ${sourceCode.getText(node.extends[0])}`, - ), - ]; - } - - return null; - }, + fix: fixer => + fixer.replaceText( + node, + `type ${sourceCode.getText(node.id)} = ${sourceCode.getText( + extend[0], + )}`, + ), }); } } diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 32538271b948..0cf9542e88a9 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -13,7 +13,7 @@ export default util.createRule({ name: 'no-floating-promises', meta: { docs: { - description: 'Requires Promise-like values to be handled appropriately.', + description: 'Requires Promise-like values to be handled appropriately', category: 'Best Practices', recommended: false, requiresTypeChecking: true, diff --git a/packages/eslint-plugin/src/rules/no-implied-eval.ts b/packages/eslint-plugin/src/rules/no-implied-eval.ts new file mode 100644 index 000000000000..fe50beba0502 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-implied-eval.ts @@ -0,0 +1,141 @@ +import * as ts from 'typescript'; +import { + TSESTree, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; +import * as util from '../util'; + +const FUNCTION_CONSTRUCTOR = 'Function'; +const EVAL_LIKE_METHODS = new Set([ + 'setImmediate', + 'setInterval', + 'setTimeout', + 'execScript', +]); + +export default util.createRule({ + name: 'no-implied-eval', + meta: { + docs: { + description: 'Disallow the use of `eval()`-like methods', + category: 'Best Practices', + recommended: false, + requiresTypeChecking: true, + }, + messages: { + noImpliedEvalError: 'Implied eval. Consider passing a function.', + noFunctionConstructor: + 'Implied eval. Do not use the Function constructor to create functions.', + }, + schema: [], + type: 'suggestion', + }, + defaultOptions: [], + create(context) { + const parserServices = util.getParserServices(context); + const checker = parserServices.program.getTypeChecker(); + + function getCalleeName( + node: TSESTree.LeftHandSideExpression, + ): string | null { + if (node.type === AST_NODE_TYPES.Identifier) { + return node.name; + } + + if ( + node.type === AST_NODE_TYPES.MemberExpression && + node.object.type === AST_NODE_TYPES.Identifier && + node.object.name === 'window' + ) { + if (node.property.type === AST_NODE_TYPES.Identifier) { + return node.property.name; + } + + if ( + node.property.type === AST_NODE_TYPES.Literal && + typeof node.property.value === 'string' + ) { + return node.property.value; + } + } + + return null; + } + + function isFunctionType(node: TSESTree.Node): boolean { + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); + const type = checker.getTypeAtLocation(tsNode); + const symbol = type.getSymbol(); + + if ( + symbol?.flags === ts.SymbolFlags.Function || + symbol?.flags === ts.SymbolFlags.Method + ) { + return true; + } + + const signatures = checker.getSignaturesOfType( + type, + ts.SignatureKind.Call, + ); + + if (signatures.length) { + const [{ declaration }] = signatures; + return declaration?.kind === ts.SyntaxKind.FunctionType; + } + + return false; + } + + function isFunction(node: TSESTree.Node): boolean { + switch (node.type) { + case AST_NODE_TYPES.ArrowFunctionExpression: + case AST_NODE_TYPES.FunctionDeclaration: + case AST_NODE_TYPES.FunctionExpression: + return true; + + case AST_NODE_TYPES.MemberExpression: + case AST_NODE_TYPES.Identifier: + return isFunctionType(node); + + case AST_NODE_TYPES.CallExpression: + return ( + (node.callee.type === AST_NODE_TYPES.Identifier && + node.callee.name === 'bind') || + isFunctionType(node) + ); + + default: + return false; + } + } + + function checkImpliedEval( + node: TSESTree.NewExpression | TSESTree.CallExpression, + ): void { + const calleeName = getCalleeName(node.callee); + if (calleeName === null) { + return; + } + + if (calleeName === FUNCTION_CONSTRUCTOR) { + context.report({ node, messageId: 'noFunctionConstructor' }); + return; + } + + if (node.arguments.length === 0) { + return; + } + + const [handler] = node.arguments; + if (EVAL_LIKE_METHODS.has(calleeName) && !isFunction(handler)) { + context.report({ node: handler, messageId: 'noImpliedEvalError' }); + } + } + + return { + NewExpression: checkImpliedEval, + CallExpression: checkImpliedEval, + }; + }, +}); diff --git a/packages/eslint-plugin/src/rules/no-magic-numbers.ts b/packages/eslint-plugin/src/rules/no-magic-numbers.ts index bf5cfdaa93fc..b4791614cda2 100644 --- a/packages/eslint-plugin/src/rules/no-magic-numbers.ts +++ b/packages/eslint-plugin/src/rules/no-magic-numbers.ts @@ -17,7 +17,7 @@ export default util.createRule({ meta: { type: 'suggestion', docs: { - description: 'Disallows magic numbers', + description: 'Disallow magic numbers', category: 'Best Practices', recommended: false, }, diff --git a/packages/eslint-plugin/src/rules/no-misused-new.ts b/packages/eslint-plugin/src/rules/no-misused-new.ts index 67d00b131f0e..d0331e19fa35 100644 --- a/packages/eslint-plugin/src/rules/no-misused-new.ts +++ b/packages/eslint-plugin/src/rules/no-misused-new.ts @@ -22,7 +22,7 @@ export default util.createRule({ defaultOptions: [], create(context) { /** - * @param {ASTNode} node type to be inspected. + * @param node type to be inspected. * @returns name of simple type or null */ function getTypeReferenceName( @@ -48,8 +48,8 @@ export default util.createRule({ } /** - * @param {ASTNode} parent parent node. - * @param {ASTNode} returnType type to be compared + * @param parent parent node. + * @param returnType type to be compared */ function isMatchingParentType( parent: undefined | TSESTree.Node, diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts index 02aca84e3151..d8e22e9d2196 100644 --- a/packages/eslint-plugin/src/rules/no-type-alias.ts +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -218,8 +218,7 @@ export default util.createRule({ /** * Validates the node looking for aliases, callbacks and literals. - * @param node the node to be validated. - * @param compositionType the type of composition this alias is part of (null if not + * @param type the type of composition this alias is part of (null if not * part of a composition) * @param isTopLevel a flag indicating this is the top level node. */ diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index ba77f30e1f9f..e7f8e0306798 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -72,7 +72,7 @@ export type MessageId = | 'never' | 'neverOptionalChain'; export default createRule({ - name: 'no-unnecessary-conditionals', + name: 'no-unnecessary-condition', meta: { type: 'suggestion', docs: { diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts index d3f14037d537..a32cb94728c2 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts @@ -17,7 +17,7 @@ export default util.createRule<[], MessageIds>({ meta: { docs: { description: - 'Warns if an explicitly specified type argument is the default for that type parameter', + 'Enforces that type arguments will not be used if not required', category: 'Best Practices', recommended: false, requiresTypeChecking: true, @@ -110,6 +110,10 @@ function getTypeParametersFromType( const sym = getAliasedSymbol(symAtLocation, checker); + if (!sym.declarations) { + return undefined; + } + return findFirstResult(sym.declarations, decl => tsutils.isClassLikeDeclaration(decl) || ts.isTypeAliasDeclaration(decl) || diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index 61cbbcb99b9c..6696b127c10b 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -245,7 +245,7 @@ export default util.createRule({ node: TSESTree.TSTypeAssertion | TSESTree.TSAsExpression, ): void { if ( - options?.typesToIgnore?.includes( + options.typesToIgnore?.includes( sourceCode.getText(node.typeAnnotation), ) ) { 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 88ccd3e4a953..830a245760a1 100644 --- a/packages/eslint-plugin/src/rules/no-untyped-public-signature.ts +++ b/packages/eslint-plugin/src/rules/no-untyped-public-signature.ts @@ -9,11 +9,10 @@ type MessageIds = 'noReturnType' | 'untypedParameter'; type Options = [{ ignoredMethods: string[] }]; export default util.createRule({ - name: 'no-unused-public-signature', + name: 'no-untyped-public-signature', meta: { docs: { - description: - 'Requires that all public method arguments and return type will be explicitly typed', + description: 'Disallow untyped public methods', category: 'Best Practices', recommended: false, }, 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 dc3f25494298..db21c42570c1 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars-experimental.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars-experimental.ts @@ -26,7 +26,7 @@ export default util.createRule({ meta: { type: 'problem', docs: { - description: 'Disallow unused variables and arguments.', + description: 'Disallow unused variables and arguments', category: 'Best Practices', recommended: false, requiresTypeChecking: true, diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts index fea08554c3ad..e62033603b53 100644 --- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts +++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts @@ -10,7 +10,7 @@ export default createRule({ type: 'suggestion', docs: { description: - 'Prefer RegExp#exec() over String#match() if no global flag is provided', + 'Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided', category: 'Best Practices', recommended: 'error', requiresTypeChecking: true, diff --git a/packages/eslint-plugin/src/rules/return-await.ts b/packages/eslint-plugin/src/rules/return-await.ts index 762d697fdbb5..43d9b3e0f55e 100644 --- a/packages/eslint-plugin/src/rules/return-await.ts +++ b/packages/eslint-plugin/src/rules/return-await.ts @@ -10,7 +10,7 @@ export default util.createRule({ name: 'return-await', meta: { docs: { - description: 'Rules for awaiting returned promises', + description: 'Enforces consistent returning of awaited values', category: 'Best Practices', recommended: false, requiresTypeChecking: true, diff --git a/packages/eslint-plugin/src/rules/space-before-function-paren.ts b/packages/eslint-plugin/src/rules/space-before-function-paren.ts index 197c87f2b29b..ab2640dcc360 100644 --- a/packages/eslint-plugin/src/rules/space-before-function-paren.ts +++ b/packages/eslint-plugin/src/rules/space-before-function-paren.ts @@ -23,8 +23,7 @@ export default util.createRule({ meta: { type: 'layout', docs: { - description: - 'enforce consistent spacing before `function` definition opening parenthesis', + description: 'Enforces consistent spacing before function parenthesis', category: 'Stylistic Issues', recommended: false, }, diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index e33b9c8a39e6..0d4235e2d7d4 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -17,6 +17,7 @@ type Options = [ { ignoreRhs?: boolean; allowNullable?: boolean; + allowSafe?: boolean; }, ]; @@ -40,6 +41,9 @@ export default util.createRule({ allowNullable: { type: 'boolean', }, + allowSafe: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -52,6 +56,7 @@ export default util.createRule({ { ignoreRhs: false, allowNullable: false, + allowSafe: false, }, ], create(context, [options]) { @@ -59,9 +64,9 @@ export default util.createRule({ const checker = service.program.getTypeChecker(); /** - * Determines if the node has a boolean type. + * Determines if the node is safe for boolean type */ - function isBooleanType(node: TSESTree.Node): boolean { + function isValidBooleanNode(node: TSESTree.Node): boolean { const tsNode = service.esTreeNodeToTSNodeMap.get( node, ); @@ -79,20 +84,31 @@ export default util.createRule({ hasBoolean = true; continue; } - if (options.allowNullable) { - if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Null)) { - continue; + + if ( + tsutils.isTypeFlagSet(ty, ts.TypeFlags.Null) || + tsutils.isTypeFlagSet(ty, ts.TypeFlags.Undefined) + ) { + if (!options.allowNullable) { + return false; } - if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Undefined)) { + continue; + } + + if ( + !tsutils.isTypeFlagSet(ty, ts.TypeFlags.StringLike) && + !tsutils.isTypeFlagSet(ty, ts.TypeFlags.NumberLike) + ) { + if (options.allowSafe) { + hasBoolean = true; continue; } } - // Union variant is something else + return false; } return hasBoolean; } - return false; } @@ -106,7 +122,7 @@ export default util.createRule({ if ( node.test !== null && node.test.type !== AST_NODE_TYPES.LogicalExpression && - !isBooleanType(node.test) + !isValidBooleanNode(node.test) ) { reportNode(node.test); } @@ -119,8 +135,8 @@ export default util.createRule({ node: TSESTree.LogicalExpression, ): void { if ( - !isBooleanType(node.left) || - (!options.ignoreRhs && !isBooleanType(node.right)) + !isValidBooleanNode(node.left) || + (!options.ignoreRhs && !isValidBooleanNode(node.right)) ) { reportNode(node); } @@ -132,7 +148,7 @@ export default util.createRule({ function assertUnaryExpressionContainsBoolean( node: TSESTree.UnaryExpression, ): void { - if (!isBooleanType(node.argument)) { + if (!isValidBooleanNode(node.argument)) { reportNode(node.argument); } } diff --git a/packages/eslint-plugin/tests/fixtures/class.ts b/packages/eslint-plugin/tests/fixtures/class.ts new file mode 100644 index 000000000000..9b8aac7d7e38 --- /dev/null +++ b/packages/eslint-plugin/tests/fixtures/class.ts @@ -0,0 +1,2 @@ +// used by no-throw-literal test case to validate custom error +export class Error {} diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index 86cb1940826b..b74c06c09fd1 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -42,15 +42,15 @@ ruleTester.run('array-type', rule, { options: [{ default: 'generic' }], }, { - code: `let fooVar: Array;`, + code: 'let fooVar: Array;', options: [{ default: 'generic' }], }, { - code: `function foo (a: Array): Array {}`, + code: 'function foo (a: Array): Array {}', options: [{ default: 'generic' }], }, { - code: `let yy: number[][] = [[4, 5], [6]];`, + code: 'let yy: number[][] = [[4, 5], [6]];', options: [{ default: 'array-simple' }], }, { @@ -66,15 +66,15 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array-simple' }], }, { - code: `let fooVar: Array<(c: number) => number>;`, + code: 'let fooVar: Array<(c: number) => number>;', options: [{ default: 'array-simple' }], }, { - code: `type fooUnion = Array;`, + code: 'type fooUnion = Array;', options: [{ default: 'array-simple' }], }, { - code: `type fooIntersection = Array;`, + code: 'type fooIntersection = Array;', options: [{ default: 'array-simple' }], }, { @@ -91,11 +91,11 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array-simple' }], }, { - code: `let yy: number[][] = [[4, 5], [6]];`, + code: 'let yy: number[][] = [[4, 5], [6]];', options: [{ default: 'array' }], }, { - code: `let ya = [[1, "2"]] as[number, string][];`, + code: 'let ya = [[1, "2"]] as[number, string][];', options: [{ default: 'array' }], }, { @@ -111,15 +111,15 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array' }], }, { - code: `let barVar: ((c: number) => number)[];`, + code: 'let barVar: ((c: number) => number)[];', options: [{ default: 'array' }], }, { - code: `type barUnion = (string|number|boolean)[];`, + code: 'type barUnion = (string|number|boolean)[];', options: [{ default: 'array' }], }, { - code: `type barIntersection = (string & number)[];`, + code: 'type barIntersection = (string & number)[];', options: [{ default: 'array' }], }, { @@ -134,15 +134,15 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array' }], }, { - code: `let z: Array = [3, "4"];`, + code: 'let z: Array = [3, "4"];', options: [{ default: 'generic' }], }, { - code: `let xx: Array> = [[1, 2], [3]];`, + code: 'let xx: Array> = [[1, 2], [3]];', options: [{ default: 'generic' }], }, { - code: `type Arr = Array;`, + code: 'type Arr = Array;', options: [{ default: 'generic' }], }, { @@ -158,15 +158,15 @@ ruleTester.run('array-type', rule, { options: [{ default: 'generic' }], }, { - code: `let fooVar: Array<(c: number) => number>;`, + code: 'let fooVar: Array<(c: number) => number>;', options: [{ default: 'generic' }], }, { - code: `type fooUnion = Array;`, + code: 'type fooUnion = Array;', options: [{ default: 'generic' }], }, { - code: `type fooIntersection = Array;`, + code: 'type fooIntersection = Array;', options: [{ default: 'generic' }], }, { @@ -189,15 +189,15 @@ ruleTester.run('array-type', rule, { options: [{ default: 'array', readonly: 'generic' }], }, { - code: `let a: Array = []`, + code: 'let a: Array = []', options: [{ default: 'generic', readonly: 'array' }], }, { - code: `let a: readonly number[] = []`, + code: 'let a: readonly number[] = []', options: [{ default: 'generic', readonly: 'array' }], }, { - code: `let a: readonly Array[] = [[]]`, + code: 'let a: readonly Array[] = [[]]', options: [{ default: 'generic', readonly: 'array' }], }, ], @@ -313,8 +313,27 @@ ruleTester.run('array-type', rule, { ], }, { - code: `let x: Array = [undefined] as undefined[];`, - output: `let x: undefined[] = [undefined] as undefined[];`, + code: 'let a: Array<>[] = []', + output: 'let a: any[][] = []', + options: [{ default: 'array-simple' }], + errors: [ + { + messageId: 'errorStringGenericSimple', + data: { type: 'T' }, + line: 1, + column: 8, + }, + { + messageId: 'errorStringArraySimple', + data: { type: 'any' }, + line: 1, + column: 8, + }, + ], + }, + { + code: 'let x: Array = [undefined] as undefined[];', + output: 'let x: undefined[] = [undefined] as undefined[];', options: [{ default: 'array-simple' }], errors: [ { @@ -326,8 +345,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: `let xx: Array = [];`, - output: `let xx: object[] = [];`, + code: 'let xx: Array = [];', + output: 'let xx: object[] = [];', options: [{ default: 'array-simple' }], errors: [ { @@ -339,8 +358,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: `let y: string[] = >["2"];`, - output: `let y: string[] = ["2"];`, + code: 'let y: string[] = >["2"];', + output: 'let y: string[] = ["2"];', options: [{ default: 'array-simple' }], errors: [ { @@ -352,8 +371,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: `let z: Array = [3, "4"];`, - output: `let z: any[] = [3, "4"];`, + code: 'let z: Array = [3, "4"];', + output: 'let z: any[] = [3, "4"];', options: [{ default: 'array-simple' }], errors: [ { @@ -365,8 +384,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: `let ya = [[1, "2"]] as[number, string][];`, - output: `let ya = [[1, "2"]] as Array<[number, string]>;`, + code: 'let ya = [[1, "2"]] as[number, string][];', + output: 'let ya = [[1, "2"]] as Array<[number, string]>;', options: [{ default: 'array-simple' }], errors: [ { @@ -378,8 +397,8 @@ ruleTester.run('array-type', rule, { ], }, { - code: `type Arr = Array;`, - output: `type Arr = T[];`, + code: 'type Arr = Array;', + output: 'type Arr = T[];', options: [{ default: 'array-simple' }], errors: [ { @@ -446,8 +465,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `let barVar: ((c: number) => number)[];`, - output: `let barVar: Array<(c: number) => number>;`, + code: 'let barVar: ((c: number) => number)[];', + output: 'let barVar: Array<(c: number) => number>;', options: [{ default: 'array-simple' }], errors: [ { @@ -459,8 +478,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `type barUnion = (string|number|boolean)[];`, - output: `type barUnion = Array;`, + code: 'type barUnion = (string|number|boolean)[];', + output: 'type barUnion = Array;', options: [{ default: 'array-simple' }], errors: [ { @@ -472,8 +491,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `type barIntersection = (string & number)[];`, - output: `type barIntersection = Array;`, + code: 'type barIntersection = (string & number)[];', + output: 'type barIntersection = Array;', options: [{ default: 'array-simple' }], errors: [ { @@ -485,8 +504,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `let v: Array = [{ bar: "bar" }];`, - output: `let v: fooName.BarType[] = [{ bar: "bar" }];`, + code: 'let v: Array = [{ bar: "bar" }];', + output: 'let v: fooName.BarType[] = [{ bar: "bar" }];', options: [{ default: 'array-simple' }], errors: [ { @@ -498,8 +517,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `let w: fooName.BazType[] = [["baz"]];`, - output: `let w: Array> = [["baz"]];`, + code: 'let w: fooName.BazType[] = [["baz"]];', + output: 'let w: Array> = [["baz"]];', options: [{ default: 'array-simple' }], errors: [ { @@ -511,8 +530,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `let x: Array = [undefined] as undefined[];`, - output: `let x: undefined[] = [undefined] as undefined[];`, + code: 'let x: Array = [undefined] as undefined[];', + output: 'let x: undefined[] = [undefined] as undefined[];', options: [{ default: 'array' }], errors: [ { @@ -524,8 +543,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `let y: string[] = >["2"];`, - output: `let y: string[] = ["2"];`, + code: 'let y: string[] = >["2"];', + output: 'let y: string[] = ["2"];', options: [{ default: 'array' }], errors: [ { @@ -537,8 +556,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `let z: Array = [3, "4"];`, - output: `let z: any[] = [3, "4"];`, + code: 'let z: Array = [3, "4"];', + output: 'let z: any[] = [3, "4"];', options: [{ default: 'array' }], errors: [ { @@ -550,8 +569,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `type Arr = Array;`, - output: `type Arr = T[];`, + code: 'type Arr = Array;', + output: 'type Arr = T[];', options: [{ default: 'array' }], errors: [ { @@ -616,8 +635,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `let fooVar: Array<(c: number) => number>;`, - output: `let fooVar: ((c: number) => number)[];`, + code: 'let fooVar: Array<(c: number) => number>;', + output: 'let fooVar: ((c: number) => number)[];', options: [{ default: 'array' }], errors: [ { @@ -629,8 +648,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `type fooUnion = Array;`, - output: `type fooUnion = (string|number|boolean)[];`, + code: 'type fooUnion = Array;', + output: 'type fooUnion = (string|number|boolean)[];', options: [{ default: 'array' }], errors: [ { @@ -642,8 +661,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `type fooIntersection = Array;`, - output: `type fooIntersection = (string & number)[];`, + code: 'type fooIntersection = Array;', + output: 'type fooIntersection = (string & number)[];', options: [{ default: 'array' }], errors: [ { @@ -655,7 +674,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `let fooVar: Array[];`, + code: 'let fooVar: Array[];', options: [{ default: 'array' }], errors: [ { @@ -667,7 +686,7 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `let fooVar: Array[];`, + code: 'let fooVar: Array[];', options: [{ default: 'array-simple' }], errors: [ { @@ -679,8 +698,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `let x: Array = [1] as number[];`, - output: `let x: Array = [1] as Array;`, + code: 'let x: Array = [1] as number[];', + output: 'let x: Array = [1] as Array;', options: [{ default: 'generic' }], errors: [ { @@ -692,8 +711,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `let y: string[] = >["2"];`, - output: `let y: Array = >["2"];`, + code: 'let y: string[] = >["2"];', + output: 'let y: Array = >["2"];', options: [{ default: 'generic' }], errors: [ { @@ -705,8 +724,8 @@ let yyyy: Arr[][]> = [[[["2"]]]];`, ], }, { - code: `let ya = [[1, "2"]] as[number, string][];`, - output: `let ya = [[1, "2"]] as Array<[number, string]>;`, + code: 'let ya = [[1, "2"]] as[number, string][];', + output: 'let ya = [[1, "2"]] as Array<[number, string]>;', options: [{ default: 'generic' }], errors: [ { @@ -771,8 +790,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `let barVar: ((c: number) => number)[];`, - output: `let barVar: Array<(c: number) => number>;`, + code: 'let barVar: ((c: number) => number)[];', + output: 'let barVar: Array<(c: number) => number>;', options: [{ default: 'generic' }], errors: [ { @@ -784,8 +803,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `type barUnion = (string|number|boolean)[];`, - output: `type barUnion = Array;`, + code: 'type barUnion = (string|number|boolean)[];', + output: 'type barUnion = Array;', options: [{ default: 'generic' }], errors: [ { @@ -797,8 +816,8 @@ let yyyy: Arr>>> = [[[["2"]]]];`, ], }, { - code: `type barIntersection = (string & number)[];`, - output: `type barIntersection = Array;`, + code: 'type barIntersection = (string & number)[];', + output: 'type barIntersection = Array;', options: [{ default: 'generic' }], errors: [ { @@ -1001,104 +1020,104 @@ class Foo extends Bar implements Baz { ); testOutput( 'array-simple', - `let xx: Array> = [[1, 2], [3]];`, - `let xx: number[][] = [[1, 2], [3]];`, + 'let xx: Array> = [[1, 2], [3]];', + 'let xx: number[][] = [[1, 2], [3]];', ); testOutput( 'array', - `let xx: Array> = [[1, 2], [3]];`, - `let xx: number[][] = [[1, 2], [3]];`, + 'let xx: Array> = [[1, 2], [3]];', + 'let xx: number[][] = [[1, 2], [3]];', ); testOutput( 'generic', - `let yy: number[][] = [[4, 5], [6]];`, - `let yy: Array> = [[4, 5], [6]];`, + 'let yy: number[][] = [[4, 5], [6]];', + 'let yy: Array> = [[4, 5], [6]];', ); // readonly testOutput( 'generic', - `let x: readonly number[][]`, - `let x: ReadonlyArray>`, + 'let x: readonly number[][]', + 'let x: ReadonlyArray>', ); testOutput( 'generic', - `let x: readonly (readonly number[])[]`, - `let x: ReadonlyArray>`, + 'let x: readonly (readonly number[])[]', + 'let x: ReadonlyArray>', ); testOutput( 'array', - `let x: ReadonlyArray>`, - `let x: readonly number[][]`, + 'let x: ReadonlyArray>', + 'let x: readonly number[][]', ); testOutput( 'array', - `let x: ReadonlyArray>`, - `let x: readonly (readonly number[])[]`, + 'let x: ReadonlyArray>', + 'let x: readonly (readonly number[])[]', ); testOutput( 'array', - `let x: ReadonlyArray`, - `let x: readonly (readonly number[])[]`, + 'let x: ReadonlyArray', + 'let x: readonly (readonly number[])[]', ); testOutput( 'array', - `let x: ReadonlyArray`, - `let x: ReadonlyArray`, + 'let x: ReadonlyArray', + 'let x: ReadonlyArray', 'generic', ); testOutput( 'array', - `let a: string[] = []`, - `let a: string[] = []`, + 'let a: string[] = []', + 'let a: string[] = []', 'generic', ); testOutput( 'array', - `let a: readonly number[][] = []`, - `let a: ReadonlyArray = []`, + 'let a: readonly number[][] = []', + 'let a: ReadonlyArray = []', 'generic', ); testOutput( 'generic', - `let a: string[] = []`, - `let a: Array = []`, + 'let a: string[] = []', + 'let a: Array = []', 'array', ); testOutput( 'generic', - `let a: readonly number[][] = []`, - `let a: readonly Array[] = []`, + 'let a: readonly number[][] = []', + 'let a: readonly Array[] = []', 'array', ); testOutput( 'generic', - `type T = readonly(string)[]`, - `type T = ReadonlyArray`, + 'type T = readonly(string)[]', + 'type T = ReadonlyArray', 'generic', ); testOutput( 'generic', - `let a: readonly(readonly string[])[] = []`, - `let a: ReadonlyArray> = []`, + 'let a: readonly(readonly string[])[] = []', + 'let a: ReadonlyArray> = []', 'generic', ); testOutput( 'generic', - `type T = readonly(readonly string[])[]`, - `type T = ReadonlyArray>`, + 'type T = readonly(readonly string[])[]', + 'type T = ReadonlyArray>', 'generic', ); testOutput( 'generic', - `type T = readonly (readonly string[])[]`, - `type T = ReadonlyArray>`, + 'type T = readonly (readonly string[])[]', + 'type T = ReadonlyArray>', 'generic', ); testOutput( 'generic', - `type T = readonly (readonly string[])[]`, - `type T = ReadonlyArray>`, + 'type T = readonly (readonly string[])[]', + 'type T = ReadonlyArray>', 'generic', ); }); diff --git a/packages/eslint-plugin/tests/rules/class-name-casing.test.ts b/packages/eslint-plugin/tests/rules/class-name-casing.test.ts index 7c89c699d5eb..11f6dc8b8889 100644 --- a/packages/eslint-plugin/tests/rules/class-name-casing.test.ts +++ b/packages/eslint-plugin/tests/rules/class-name-casing.test.ts @@ -34,6 +34,14 @@ ruleTester.run('class-name-casing', rule, { 'class ClassNameWithUnicodeÈ {}', 'class ÈClassNameWithUnicode {}', 'class ClassNameWithæUnicode {}', + // Following test cases are valid, but no one is going to write code like this + 'var { bar } = class { static bar() { return 2 } }', + `var [ bar ] = class { + static [Symbol.iterator]() { + return { next: () => ({ value: 1, done: false}) } + } + } + `, ], invalid: [ @@ -164,7 +172,7 @@ ruleTester.run('class-name-casing', rule, { ], }, { - code: `class æInvalidClassNameWithUnicode {}`, + code: 'class æInvalidClassNameWithUnicode {}', errors: [ { messageId: 'notPascalCased', diff --git a/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts b/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts index 7eb6bcd076b2..b5477c062a10 100644 --- a/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-member-accessibility.test.ts @@ -252,11 +252,7 @@ class Test { constructor(public foo: number){} } `, - options: [ - { - accessibility: 'no-public', - }, - ], + options: [{ accessibility: 'no-public' }], }, { filename: 'test.ts', @@ -267,11 +263,7 @@ class Test { } } `, - options: [ - { - ignoredMethodNames: ['getX'], - }, - ], + options: [{ ignoredMethodNames: ['getX'] }], }, { filename: 'test.ts', @@ -282,11 +274,7 @@ class Test { } } `, - options: [ - { - ignoredMethodNames: ['getX'], - }, - ], + options: [{ ignoredMethodNames: ['getX'] }], }, { filename: 'test.ts', @@ -297,11 +285,7 @@ class Test { } } `, - options: [ - { - ignoredMethodNames: ['getX'], - }, - ], + options: [{ ignoredMethodNames: ['getX'] }], }, { filename: 'test.ts', @@ -312,11 +296,29 @@ class Test { } } `, - options: [ - { - ignoredMethodNames: ['getX'], - }, - ], + options: [{ ignoredMethodNames: ['getX'] }], + }, + { + filename: 'test.ts', + code: 'class Test { x = 2 }', + options: [{ overrides: { properties: 'off' } }], + }, + { + filename: 'test.ts', + code: 'class Test { private x = 2 }', + options: [{ overrides: { properties: 'explicit' } }], + }, + { + filename: 'test.ts', + code: `class Test { + x = 2 + private x = 2 + }`, + options: [{ overrides: { properties: 'no-public' } }], + }, + { + code: 'class Test { constructor(private { x }: any[]) { }}', + options: [{ accessibility: 'no-public' }], }, ], invalid: [ @@ -656,5 +658,53 @@ class Test { }, ], }, + { + filename: 'test.ts', + code: 'class Test { x = 2 }', + options: [ + { + accessibility: 'off', + overrides: { properties: 'explicit' }, + }, + ], + errors: [ + { + messageId: 'missingAccessibility', + line: 1, + column: 14, + }, + ], + }, + { + filename: 'test.ts', + code: `class Test { + public x = 2 + private x = 2 + }`, + options: [ + { + accessibility: 'off', + overrides: { properties: 'no-public' }, + }, + ], + errors: [ + { + messageId: 'unwantedPublicAccessibility', + line: 2, + column: 9, + }, + ], + }, + { + code: 'class Test { constructor(public ...x: any[]) { }}', + options: [{ accessibility: 'explicit' }], + errors: [ + { + messageId: 'missingAccessibility', + line: 1, + column: 14, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-implied-eval.test.ts b/packages/eslint-plugin/tests/rules/no-implied-eval.test.ts new file mode 100644 index 000000000000..947044727d51 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-implied-eval.test.ts @@ -0,0 +1,758 @@ +import rule from '../../src/rules/no-implied-eval'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; + +const rootDir = getFixturesRootDir(); +const ruleTester = new RuleTester({ + parserOptions: { + tsconfigRootDir: rootDir, + ecmaVersion: 2015, + project: './tsconfig.json', + }, + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-implied-eval', rule, { + valid: [ + `foo.setImmediate(null);`, + `foo.setInterval(null);`, + `foo.execScript(null);`, + `foo.setTimeout(null);`, + `foo()`, + `(function(){ })()`, + + `setTimeout(() => {}, 0);`, + `window.setTimeout(() => {}, 0);`, + `window['setTimeout'](() => {}, 0);`, + + `setInterval(() => {}, 0);`, + `window.setInterval(() => {}, 0);`, + `window['setInterval'](() => {}, 0);`, + + `setImmediate(() => {});`, + `window.setImmediate(() => {});`, + `window['setImmediate'](() => {});`, + + `execScript(() => {});`, + `window.execScript(() => {});`, + `window['execScript'](() => {});`, + + { + code: ` +const foo = () => {}; + +setTimeout(foo, 0); +setInterval(foo, 0); +setImmediate(foo); +execScript(foo); + `, + }, + { + code: ` +const foo = function () {}; + +setTimeout(foo, 0); +setInterval(foo, 0); +setImmediate(foo); +execScript(foo); + `, + }, + { + code: ` +function foo() {}; + +setTimeout(foo, 0); +setInterval(foo, 0); +setImmediate(foo); +execScript(foo); + `, + }, + { + code: ` +const foo = { + fn: () => {}, +} + +setTimeout(foo.fn, 0); +setInterval(foo.fn, 0); +setImmediate(foo.fn); +execScript(foo.fn); + `, + }, + { + code: ` +const foo = { + fn: function () {}, +} + +setTimeout(foo.fn, 0); +setInterval(foo.fn, 0); +setImmediate(foo.fn); +execScript(foo.fn); + `, + }, + { + code: ` +const foo = { + fn: function foo() {}, +} + +setTimeout(foo.fn, 0); +setInterval(foo.fn, 0); +setImmediate(foo.fn); +execScript(foo.fn); + `, + }, + { + code: ` +const foo = { + fn() {}, +} + +setTimeout(foo.fn, 0); +setInterval(foo.fn, 0); +setImmediate(foo.fn); +execScript(foo.fn); + `, + }, + { + code: ` +const foo = { + fn: () => {}, +} +const fn = 'fn'; + +setTimeout(foo[fn], 0); +setInterval(foo[fn], 0); +setImmediate(foo[fn]); +execScript(foo[fn]); + `, + }, + { + code: ` +const foo = { + fn: () => {}, +} + +setTimeout(foo['fn'], 0); +setInterval(foo['fn'], 0); +setImmediate(foo['fn']); +execScript(foo['fn']); + `, + }, + { + code: ` +const foo: () => void = () => { +}; + +setTimeout(foo, 0); +setInterval(foo, 0); +setImmediate(foo); +execScript(foo); + `, + }, + { + code: ` +const foo: (() => () => void) = () => { + return () => {}; +} + +setTimeout(foo(), 0); +setInterval(foo(), 0); +setImmediate(foo()); +execScript(foo()); + `, + }, + { + code: ` +const foo: (() => () => void) = () => () => {}; + +setTimeout(foo(), 0); +setInterval(foo(), 0); +setImmediate(foo()); +execScript(foo()); + `, + }, + { + code: ` +const foo = () => () => {}; + +setTimeout(foo(), 0); +setInterval(foo(), 0); +setImmediate(foo()); +execScript(foo()); + `, + }, + { + code: ` +const foo = function foo () { + return function foo() {} +} + +setTimeout(foo(), 0); +setInterval(foo(), 0); +setImmediate(foo()); +execScript(foo()); + `, + }, + { + code: ` +const foo = function () { + return function () { + return ''; + } +} + +setTimeout(foo(), 0); +setInterval(foo(), 0); +setImmediate(foo()); +execScript(foo()); + `, + }, + { + code: ` +const foo: (() => () => void) = function foo () { + return function foo() {} +} + +setTimeout(foo(), 0); +setInterval(foo(), 0); +setImmediate(foo()); +execScript(foo()); + `, + }, + { + code: ` +function foo() { + return function foo() { + return () => {}; + }; +} + +setTimeout(foo()(), 0); +setInterval(foo()(), 0); +setImmediate(foo()()); +execScript(foo()()); + `, + }, + { + code: ` +class Foo { + static fn = () => {}; +} + +setTimeout(Foo.fn, 0); +setInterval(Foo.fn, 0); +setImmediate(Foo.fn); +execScript(Foo.fn); + `, + }, + { + code: ` +class Foo { + fn() {} +} + +const foo = new Foo(); + +setTimeout(foo.fn, 0); +setInterval(foo.fn, 0); +setImmediate(foo.fn); +execScript(foo.fn); + `, + }, + { + code: ` +class Foo { + fn() {} +} +const foo = new Foo(); +const fn = foo.fn; + +setTimeout(fn.bind(null), 0); +setInterval(fn.bind(null), 0); +setImmediate(fn.bind(null)); +execScript(fn.bind(null)); + `, + }, + { + code: ` +const fn = (foo: () => void) => { + setTimeout(foo, 0); + setInterval(foo, 0); + setImmediate(foo); + execScript(foo); +} + `, + }, + ], + + invalid: [ + { + code: ` +setTimeout('x = 1', 0); +setInterval('x = 1', 0); +setImmediate('x = 1'); +execScript('x = 1'); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 2, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 3, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 4, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 12, + }, + ], + }, + { + code: ` +setTimeout(undefined, 0); +setInterval(undefined, 0); +setImmediate(undefined); +execScript(undefined); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 2, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 3, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 4, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 12, + }, + ], + }, + { + code: ` +setTimeout(1 + '' + (() => {}), 0); +setInterval(1 + '' + (() => {}), 0); +setImmediate(1 + '' + (() => {})); +execScript(1 + '' + (() => {})); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 2, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 3, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 4, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 12, + }, + ], + }, + { + code: ` +const foo = 'x = 1'; + +setTimeout(foo, 0); +setInterval(foo, 0); +setImmediate(foo); +execScript(foo); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 4, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 6, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 7, + column: 12, + }, + ], + }, + { + code: ` +const foo = function () { + return 'x + 1'; +}; + +setTimeout(foo(), 0); +setInterval(foo(), 0); +setImmediate(foo()); +execScript(foo()); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 6, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 7, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 8, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 9, + column: 12, + }, + ], + }, + { + code: ` +const foo = function () { + return () => 'x + 1'; +}; + +setTimeout(foo()(), 0); +setInterval(foo()(), 0); +setImmediate(foo()()); +execScript(foo()()); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 6, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 7, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 8, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 9, + column: 12, + }, + ], + }, + { + code: ` +const fn = function () {} + +setTimeout(fn + '', 0); +setInterval(fn + '', 0); +setImmediate(fn + ''); +execScript(fn + ''); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 4, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 6, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 7, + column: 12, + }, + ], + }, + { + code: ` +const foo: string = 'x + 1'; + +setTimeout(foo, 0); +setInterval(foo, 0); +setImmediate(foo); +execScript(foo); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 4, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 6, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 7, + column: 12, + }, + ], + }, + { + code: ` +const foo = new String('x + 1'); + +setTimeout(foo, 0); +setInterval(foo, 0); +setImmediate(foo); +execScript(foo); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 4, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 6, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 7, + column: 12, + }, + ], + }, + { + code: ` +const foo = 'x + 1'; + +setTimeout(foo as any, 0); +setInterval(foo as any, 0); +setImmediate(foo as any); +execScript(foo as any); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 4, + column: 12, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 13, + }, + { + messageId: 'noImpliedEvalError', + line: 6, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 7, + column: 12, + }, + ], + }, + { + code: ` +const fn = (foo: string | any) => { + setTimeout(foo, 0); + setInterval(foo, 0); + setImmediate(foo); + execScript(foo); +} + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 3, + column: 14, + }, + { + messageId: 'noImpliedEvalError', + line: 4, + column: 15, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 16, + }, + { + messageId: 'noImpliedEvalError', + line: 6, + column: 14, + }, + ], + }, + { + code: ` +window.setTimeout(\`\`, 0); +window['setTimeout'](\`\`, 0); + +window.setInterval(\`\`, 0); +window['setInterval'](\`\`, 0); + +window.setImmediate(\`\`); +window['setImmediate'](\`\`); + +window.execScript(\`\`); +window['execScript'](\`\`); + `, + errors: [ + { + messageId: 'noImpliedEvalError', + line: 2, + column: 19, + }, + { + messageId: 'noImpliedEvalError', + line: 3, + column: 22, + }, + { + messageId: 'noImpliedEvalError', + line: 5, + column: 20, + }, + { + messageId: 'noImpliedEvalError', + line: 6, + column: 23, + }, + { + messageId: 'noImpliedEvalError', + line: 8, + column: 21, + }, + { + messageId: 'noImpliedEvalError', + line: 9, + column: 24, + }, + { + messageId: 'noImpliedEvalError', + line: 11, + column: 19, + }, + { + messageId: 'noImpliedEvalError', + line: 12, + column: 22, + }, + ], + }, + { + code: `const fn = Function()`, + errors: [ + { + messageId: 'noFunctionConstructor', + line: 1, + column: 12, + }, + ], + }, + { + code: `const fn = new Function('a', 'b', 'return a + b');`, + errors: [ + { + messageId: 'noFunctionConstructor', + line: 1, + column: 12, + }, + ], + }, + { + code: `const fn = window.Function();`, + errors: [ + { + messageId: 'noFunctionConstructor', + line: 1, + column: 12, + }, + ], + }, + { + code: `const fn = new window.Function();`, + errors: [ + { + messageId: 'noFunctionConstructor', + line: 1, + column: 12, + }, + ], + }, + { + code: `const fn = window['Function']();`, + errors: [ + { + messageId: 'noFunctionConstructor', + line: 1, + column: 12, + }, + ], + }, + { + code: `const fn = new window['Function']();`, + errors: [ + { + messageId: 'noFunctionConstructor', + line: 1, + column: 12, + }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin/tests/rules/no-misused-new.test.ts b/packages/eslint-plugin/tests/rules/no-misused-new.test.ts index 2cc784d4e23d..414d86245d5f 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-new.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-new.test.ts @@ -14,40 +14,22 @@ declare abstract class C { get new (); bar(); } - `, - ` -class C { - constructor(); -} - `, - ` -class C { - constructor() {} -} - `, + `, + 'class C { constructor(); }', + 'const foo = class { constructor(); }', + 'const foo = class { new(): X }', // OK if there's a body - ` -class C { - new() {} -} - `, + 'class C { new() {} }', + 'class C { constructor() {} }', + 'const foo = class { new() {} }', + 'const foo = class { constructor() {} }', // OK if return type is not the interface. - ` -interface I { - new(): {}; -} - `, + 'interface I { new(): {}; }', // 'new' OK in type literal (we don't know the type name) - ` -type T = { - new(): T; -} - `, - ` -export default class { - constructor(); -} - `, + 'type T = { new(): T; }', + 'export default class { constructor(); }', + 'interface foo { new(): bar; }', + "interface foo { new(): 'x'; }", ], invalid: [ { @@ -128,5 +110,19 @@ declare abstract class C { }, ], }, + { + code: ` +interface I { + constructor(): ''; +} +`, + errors: [ + { + messageId: 'errorMessageInterface', + line: 3, + column: 5, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-throw-literal.test.ts b/packages/eslint-plugin/tests/rules/no-throw-literal.test.ts index 8cec2d66d9dd..359c2abf3c51 100644 --- a/packages/eslint-plugin/tests/rules/no-throw-literal.test.ts +++ b/packages/eslint-plugin/tests/rules/no-throw-literal.test.ts @@ -4,6 +4,7 @@ import { RuleTester, getFixturesRootDir } from '../RuleTester'; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018, + sourceType: 'module', tsconfigRootDir: getFixturesRootDir(), project: './tsconfig.json', }, @@ -12,117 +13,72 @@ const ruleTester = new RuleTester({ ruleTester.run('no-throw-literal', rule, { valid: [ - { - code: `throw new Error();`, - }, - { - code: `throw new Error('error');`, - }, - { - code: `throw Error('error');`, - }, - { - code: ` + 'throw new Error();', + "throw new Error('error');", + "throw Error('error');", + ` const e = new Error(); throw e; - `, - }, - { - code: ` + `, + ` try { throw new Error(); } catch (e) { throw e; } - `, - }, - { - code: ` + `, + ` function foo() { return new Error(); } - throw foo(); - `, - }, - { - code: ` + `, + ` const foo = { bar: new Error() } - throw foo.bar; - `, - }, - { - code: ` + `, + ` const foo = { bar: new Error() } throw foo['bar']; - `, - }, - { - code: ` + `, + ` const foo = { bar: new Error() } const bar = 'bar'; throw foo[bar]; - `, - }, - { - code: ` + `, + ` class CustomError extends Error {}; throw new CustomError(); - `, - }, - { - code: ` + `, + ` class CustomError1 extends Error {} class CustomError2 extends CustomError1 {} throw new CustomError(); - `, - }, - { - code: `throw foo = new Error();`, - }, - { - code: `throw 1, 2, new Error();`, - }, - { - code: `throw 'literal' && new Error();`, - }, - { - code: `throw new Error() || 'literal'`, - }, - { - code: `throw foo ? new Error() : 'literal';`, - }, - { - code: `throw foo ? 'literal' : new Error();`, - }, - { - code: ` -function* foo() { - let index = 0; - throw yield index++; -} - `, - }, - { - code: ` -async function foo() { - throw await bar; -} - `, - }, + `, + 'throw foo = new Error();', + 'throw 1, 2, new Error();', + "throw 'literal' && new Error();", + "throw new Error() || 'literal'", + "throw foo ? new Error() : 'literal';", + "throw foo ? 'literal' : new Error();", + 'function* foo() { let index = 0; throw yield index++; }', + 'async function foo() { throw await bar; }', + ` +import { Error } from './missing'; +throw Error; + `, ], invalid: [ { - code: `throw undefined;`, + code: 'throw undefined;', errors: [ { messageId: 'undef', @@ -130,7 +86,7 @@ async function foo() { ], }, { - code: `throw new String('');`, + code: "throw new String('');", errors: [ { messageId: 'object', @@ -138,7 +94,7 @@ async function foo() { ], }, { - code: `throw 'error';`, + code: "throw 'error';", errors: [ { messageId: 'object', @@ -146,7 +102,7 @@ async function foo() { ], }, { - code: `throw 0;`, + code: 'throw 0;', errors: [ { messageId: 'object', @@ -154,7 +110,7 @@ async function foo() { ], }, { - code: `throw false;`, + code: 'throw false;', errors: [ { messageId: 'object', @@ -162,7 +118,7 @@ async function foo() { ], }, { - code: `throw null;`, + code: 'throw null;', errors: [ { messageId: 'object', @@ -170,7 +126,7 @@ async function foo() { ], }, { - code: `throw {};`, + code: 'throw {};', errors: [ { messageId: 'object', @@ -178,7 +134,7 @@ async function foo() { ], }, { - code: `throw 'a' + 'b';`, + code: "throw 'a' + 'b';", errors: [ { messageId: 'object', @@ -197,7 +153,7 @@ throw a + 'b'; ], }, { - code: `throw foo = 'error';`, + code: "throw foo = 'error';", errors: [ { messageId: 'object', @@ -205,7 +161,7 @@ throw a + 'b'; ], }, { - code: `throw new Error(), 1, 2, 3;`, + code: 'throw new Error(), 1, 2, 3;', errors: [ { messageId: 'object', @@ -213,7 +169,7 @@ throw a + 'b'; ], }, { - code: `throw 'literal' && 'not an Error';`, + code: "throw 'literal' && 'not an Error';", errors: [ { messageId: 'object', @@ -221,7 +177,7 @@ throw a + 'b'; ], }, { - code: `throw foo ? 'not an Error' : 'literal';`, + code: "throw foo ? 'not an Error' : 'literal';", errors: [ { messageId: 'object', @@ -319,5 +275,18 @@ throw Error; }, ], }, + { + code: ` +import { Error } from './class'; +throw new Error(); + `, + errors: [ + { + messageId: 'object', + line: 3, + column: 7, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-type-alias.test.ts b/packages/eslint-plugin/tests/rules/no-type-alias.test.ts index be5ca84b9e5b..9d6b0cd0f97f 100644 --- a/packages/eslint-plugin/tests/rules/no-type-alias.test.ts +++ b/packages/eslint-plugin/tests/rules/no-type-alias.test.ts @@ -358,7 +358,8 @@ type Foo = { options: [{ allowMappedTypes: 'in-intersections' }], }, { - code: `export type ClassValue = string | number | ClassDictionary | ClassArray | undefined | null | false;`, + code: + 'export type ClassValue = string | number | ClassDictionary | ClassArray | undefined | null | false;', options: [ { allowAliases: 'in-unions-and-intersections', @@ -2974,7 +2975,7 @@ type Foo = { }, { // https://github.com/typescript-eslint/typescript-eslint/issues/270 - code: `export type ButtonProps = JSX.IntrinsicElements['button'];`, + code: "export type ButtonProps = JSX.IntrinsicElements['button'];", errors: [ { messageId: 'noTypeAlias', @@ -3225,5 +3226,29 @@ type Foo = { }, ], }, + { + // unique symbol is not allowed in this context + code: 'type Foo = keyof [string] | unique symbol;', + errors: [ + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Tuple Types', + }, + line: 1, + column: 12, + }, + { + messageId: 'noCompositionAlias', + data: { + compositionType: 'union', + typeName: 'Unhandled', + }, + line: 1, + column: 29, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-arguments.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-arguments.test.ts index df79528403df..51c1acf3285f 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-arguments.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-arguments.test.ts @@ -6,6 +6,7 @@ const rootDir = path.join(process.cwd(), 'tests/fixtures'); const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015, + sourceType: 'module', tsconfigRootDir: rootDir, project: './tsconfig.json', }, @@ -74,6 +75,9 @@ ruleTester.run('no-unnecessary-type-arguments', rule, { class Foo extends Bar {}`, `interface Bar {} class Foo implements Bar {}`, + `import { F } from './missing'; + function bar() {} + bar>()`, ], invalid: [ { @@ -177,5 +181,20 @@ ruleTester.run('no-unnecessary-type-arguments', rule, { output: `class Bar {} class Foo extends Bar {}`, }, + { + code: `import { F } from './missing'; + function bar>() {} + bar>()`, + errors: [ + { + line: 3, + column: 13, + messageId: 'unnecessaryTypeParameter', + }, + ], + output: `import { F } from './missing'; + function bar>() {} + bar()`, + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index 73a8619b6a48..b4f079f39605 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -30,6 +30,8 @@ const foo = ({ hello: "hello" }) as PossibleTuple;`, ` type PossibleTuple = { 0: "hello", 5: "hello" }; const foo = ({ 0: "hello", 5: "hello" }) as PossibleTuple;`, + `let bar: number | undefined = x; + let foo: number = bar!;`, { code: ` type Foo = number; @@ -37,15 +39,15 @@ const foo = (3 + 5) as Foo;`, options: [{ typesToIgnore: ['Foo'] }], }, { - code: `const foo = (3 + 5) as any;`, + code: 'const foo = (3 + 5) as any;', options: [{ typesToIgnore: ['any'] }], }, { - code: `((Syntax as any).ArrayExpression = 'foo')`, + code: "((Syntax as any).ArrayExpression = 'foo')", options: [{ typesToIgnore: ['any'] }], }, { - code: `const foo = (3 + 5) as string;`, + code: 'const foo = (3 + 5) as string;', options: [{ typesToIgnore: ['string'] }], }, { diff --git a/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts b/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts index 4eb08c7e9455..0e22722afb43 100644 --- a/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-for-of.test.ts @@ -100,6 +100,12 @@ for(let x = 0; x < arr.length(); x++) {} `, ` for(let x = 0; x < arr.length; x+=11) {} +`, + ` +for(let x = arr.length; x > 1; x-=1) {} +`, + ` +for(let x = 0; x < arr.length; x*=2) {} `, ` for(let x = 0; x < arr.length; x=x+11) {} diff --git a/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts b/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts index 8d6ffbf0f7a5..256e38360d9e 100644 --- a/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts @@ -13,9 +13,9 @@ const ruleTester = new RuleTester({ ruleTester.run('prefer-readonly', rule, { valid: [ - `function ignore() { }`, - `const ignore = function () { }`, - `const ignore = () => { }`, + 'function ignore() { }', + 'const ignore = function () { }', + 'const ignore = () => { }', `const container = { member: true }; container.member;`, `const container = { member: 1 }; @@ -30,7 +30,7 @@ ruleTester.run('prefer-readonly', rule, { --container.member;`, `const container = { member: 1 }; container.member--;`, - `class TestEmpty { }`, + 'class TestEmpty { }', `class TestReadonlyStatic { private static readonly correctlyReadonlyStatic = 7; }`, diff --git a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts index f1b98a1c9411..244d8511dba4 100644 --- a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts @@ -178,6 +178,35 @@ ruleTester.run('strict-boolean-expressions', rule, { declare const x: string | null; y = x ?? 'foo'; `, + { + options: [{ allowSafe: true }], + code: ` + type TestType = { a: string; }; + const f1 = (x: boolean | TestType) => x ? 1 : 0; + const f2 = (x: true | TestType) => x ? 1 : 0; + const f3 = (x: TestType | false) => x ? 1 : 0; + `, + }, + { + options: [{ allowNullable: true, allowSafe: true }], + code: ` + type TestType = { a: string; }; + type TestType2 = { b: number; }; + const f1 = (x?: boolean | TestType) => x ? 1 : 0; + const f2 = (x: TestType | TestType2 | null) => x ? 1 : 0; + const f3 = (x?: TestType | TestType2 | null) => x ? 1 : 0; + const f4 = (x?: TestType2 | true) => x ? 1 : 0; + const f5 = (g?: (x: number) => number) => g ? g(1) : 0; + `, + }, + { + options: [{ allowNullable: true, allowSafe: true, ignoreRhs: true }], + code: ` + type TestType = { foo? : { bar?: string; }; }; + const f1 = (x?: TestType) => x && x.foo && x.foo.bar + const f2 = (g?: (x: number) => number) => g && g(1) + `, + }, ], invalid: [ @@ -925,6 +954,30 @@ ruleTester.run('strict-boolean-expressions', rule, { }, ], }, + { + errors: [ + { + messageId: 'strictBooleanExpression', + line: 2, + column: 55, + }, + { + messageId: 'strictBooleanExpression', + line: 3, + column: 37, + }, + { + messageId: 'strictBooleanExpression', + line: 4, + column: 41, + }, + ], + code: ` + const f1 = (x: boolean | null | undefined) => x ? 1 : 0; + const f2 = (x?: boolean) => x ? 1 : 0; + const f3 = (x: boolean | {}) => x ? 1 : 0; + `, + }, { options: [{ ignoreRhs: true }], errors: [ @@ -965,5 +1018,76 @@ const objAndBool = obj && bool; const f = (x?: number) => x ? 1 : 0; `, }, + { + options: [{ allowSafe: true }], + errors: [ + { + messageId: 'strictBooleanExpression', + line: 3, + column: 42, + }, + { + messageId: 'strictBooleanExpression', + line: 4, + column: 42, + }, + { + messageId: 'strictBooleanExpression', + line: 5, + column: 44, + }, + ], + code: ` + type Type = { a: string; }; + const f1 = (x: Type | string) => x ? 1 : 0; + const f2 = (x: Type | number) => x ? 1 : 0; + const f3 = (x: number | string) => x ? 1 : 0; + `, + }, + { + options: [{ allowSafe: true }], + errors: [ + { + messageId: 'strictBooleanExpression', + line: 8, + column: 34, + }, + { + messageId: 'strictBooleanExpression', + line: 9, + column: 34, + }, + ], + code: ` + enum Enum1 { + A, B, C + } + enum Enum2 { + A = 'A', B = 'B', C = 'C' + } + const f1 = (x: Enum1) => x ? 1 : 0; + const f2 = (x: Enum2) => x ? 1 : 0; + `, + }, + { + options: [{ allowNullable: true, allowSafe: true }], + errors: [ + { + messageId: 'strictBooleanExpression', + line: 3, + column: 43, + }, + { + messageId: 'strictBooleanExpression', + line: 4, + column: 49, + }, + ], + code: ` + type Type = { a: string; }; + const f1 = (x?: Type | string) => x ? 1 : 0; + const f2 = (x: Type | number | null) => x ? 1 : 0; + `, + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/triple-slash-reference.test.ts b/packages/eslint-plugin/tests/rules/triple-slash-reference.test.ts index 0785bf7a02ee..690526edf632 100644 --- a/packages/eslint-plugin/tests/rules/triple-slash-reference.test.ts +++ b/packages/eslint-plugin/tests/rules/triple-slash-reference.test.ts @@ -10,6 +10,28 @@ const ruleTester = new RuleTester({ ruleTester.run('triple-slash-reference', rule, { valid: [ + { + code: ` + // + // + // + import * as foo from "foo" + import * as bar from "bar" + import * as baz from "baz" + `, + options: [{ path: 'never', types: 'never', lib: 'never' }], + }, + { + code: ` + // + // + // + import foo = require("foo") + import bar = require("bar") + import baz = require("baz") + `, + options: [{ path: 'never', types: 'never', lib: 'never' }], + }, { code: ` /// @@ -23,26 +45,45 @@ ruleTester.run('triple-slash-reference', rule, { }, { code: ` - import * as foo from "foo" + /// + /// + /// + import foo = require("foo") + import bar = require("bar") + import baz = require("baz") `, + options: [{ path: 'always', types: 'always', lib: 'always' }], + }, + { + code: 'import * as foo from "foo"', options: [{ path: 'never' }], }, { - code: ` - import * as foo from "foo" - `, + code: 'import foo = require("foo");', + options: [{ path: 'never' }], + }, + { + code: 'import * as foo from "foo"', options: [{ types: 'never' }], }, { - code: ` - import * as foo from "foo" - `, + code: 'import foo = require("foo");', + options: [{ types: 'never' }], + }, + { + code: 'import * as foo from "foo"', options: [{ lib: 'never' }], }, { - code: ` - import * as foo from "foo" - `, + code: 'import foo = require("foo");', + options: [{ lib: 'never' }], + }, + { + code: 'import * as foo from "foo"', + options: [{ types: 'prefer-import' }], + }, + { + code: 'import foo = require("foo");', options: [{ types: 'prefer-import' }], }, { @@ -92,7 +133,7 @@ import foo = require("foo"); ], }, { - code: `/// `, + code: '/// ', options: [{ path: 'never' }], errors: [ { @@ -103,7 +144,7 @@ import foo = require("foo"); ], }, { - code: `/// `, + code: '/// ', options: [{ types: 'never' }], errors: [ { @@ -114,7 +155,7 @@ import foo = require("foo"); ], }, { - code: `/// `, + code: '/// ', options: [{ lib: 'never' }], errors: [ { diff --git a/packages/eslint-plugin/tools/validate-docs/check-for-rule-docs.ts b/packages/eslint-plugin/tools/validate-docs/check-for-rule-docs.ts index f83cd8fbe583..f52c1bccbb0d 100644 --- a/packages/eslint-plugin/tools/validate-docs/check-for-rule-docs.ts +++ b/packages/eslint-plugin/tools/validate-docs/check-for-rule-docs.ts @@ -2,26 +2,66 @@ import { TSESLint } from '@typescript-eslint/experimental-utils'; import fs from 'fs'; import path from 'path'; import { logRule } from '../log'; +import chalk from 'chalk'; +import marked from 'marked'; -function checkForRuleDocs( +function isHeading( + token: marked.Token, + depth = 1, +): token is marked.Tokens.Heading { + return token && token.type === 'heading' && token.depth === depth; +} + +export function checkForRuleDocs( rules: Record>>, ): boolean { - const ruleDocs = new Set( - fs.readdirSync(path.resolve(__dirname, '../../docs/rules')), - ); + const docsRoot = path.resolve(__dirname, '../../docs/rules'); + const ruleDocs = new Set(fs.readdirSync(docsRoot)); let hasErrors = false; Object.keys(rules).forEach(ruleName => { + const rule = rules[ruleName].meta; + const errors: string[] = []; const ruleHasDoc = ruleDocs.has(`${ruleName}.md`); - hasErrors = hasErrors || !ruleHasDoc; - logRule( - ruleHasDoc, - ruleName, - `Couldn't find file docs/rules/${ruleName}.md`, - ); + if (!ruleHasDoc) { + errors.push(`Couldn't find file docs/rules/${ruleName}.md`); + } else { + const file = fs.readFileSync( + path.join(docsRoot, `${ruleName}.md`), + 'utf-8', + ); + + const tokens = marked.lexer(file, { + gfm: true, + silent: false, + }); + + if (isHeading(tokens[0])) { + const expectedDescription = `${rule.docs.description} (\`${ruleName}\`)`; + if (tokens[0].text !== expectedDescription) { + errors.push( + 'Rule title does not match the rule metadata.', + ` Expected: ${chalk.underline(expectedDescription)}`, + ` Received: ${chalk.underline(tokens[0].text)}`, + ); + } + } else { + errors.push('Rule title not found.'); + } + } + + const ruleConfigName = /([A-Za-z-]+)\.md/.exec(rule.docs.url) ?? []; + if (ruleConfigName[1] !== ruleName) { + errors.push( + 'Name field does not match with rule name.', + ` Expected: ${chalk.underline(ruleName)}`, + ` Received: ${chalk.underline(ruleConfigName[1])}`, + ); + } + + logRule(errors.length === 0, ruleName, ...errors); + hasErrors = hasErrors || errors.length !== 0; }); return hasErrors; } - -export { checkForRuleDocs }; diff --git a/packages/eslint-plugin/tools/validate-docs/index.ts b/packages/eslint-plugin/tools/validate-docs/index.ts index 6273ff61c87b..5f2cf1fa9f8b 100644 --- a/packages/eslint-plugin/tools/validate-docs/index.ts +++ b/packages/eslint-plugin/tools/validate-docs/index.ts @@ -12,7 +12,7 @@ console.log(chalk.underline('Checking for rule docs')); hasErrors = hasErrors || checkForRuleDocs(rules); console.log(); -console.log(chalk.underline('Valdiating README.md')); +console.log(chalk.underline('Validating README.md')); const rulesTable = parseReadme(); console.log(); diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index 6fae6018c5fd..88cec1802a9d 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/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.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + +**Note:** Version bump only for package @typescript-eslint/experimental-utils + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 995f9452df89..34b7f5e91b7a 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "2.14.0", + "version": "2.15.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -36,7 +36,7 @@ }, "dependencies": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.14.0", + "@typescript-eslint/typescript-estree": "2.15.0", "eslint-scope": "^5.0.0" }, "peerDependencies": { diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index da92fe57ddd2..dddf4aacbc38 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.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) diff --git a/packages/parser/README.md b/packages/parser/README.md index bf2c67c09343..878baf600f78 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -128,7 +128,8 @@ This option allows you to provide the root directory for relative tsconfig paths Default `undefined`. -This option allows you to provide one or more additional file extensions which should be considered in the TypeScript Program compilation. E.g. a `.vue` file +This option allows you to provide one or more additional file extensions which should be considered in the TypeScript Program compilation. +The default extensions are `.ts`, `.tsx`, `.js`, and `.jsx`. Add extensions starting with `.`, followed by the file extension. E.g. for a `.vue` file use `"extraFileExtensions: [".vue"]`. ### `warnOnUnsupportedTypeScriptVersion` diff --git a/packages/parser/package.json b/packages/parser/package.json index f7e59d93d783..5827fb888ed5 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "2.14.0", + "version": "2.15.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.14.0", - "@typescript-eslint/typescript-estree": "2.14.0", + "@typescript-eslint/experimental-utils": "2.15.0", + "@typescript-eslint/typescript-estree": "2.15.0", "eslint-visitor-keys": "^1.1.0" }, "devDependencies": { "@types/glob": "^7.1.1", - "@typescript-eslint/shared-fixtures": "2.14.0", + "@typescript-eslint/shared-fixtures": "2.15.0", "glob": "*" }, "peerDependenciesMeta": { diff --git a/packages/parser/tests/lib/parser.ts b/packages/parser/tests/lib/parser.ts index 782d474afc36..4f5136b036bc 100644 --- a/packages/parser/tests/lib/parser.ts +++ b/packages/parser/tests/lib/parser.ts @@ -56,7 +56,7 @@ describe('parser', () => { errorOnUnknownASTType: false, errorOnTypeScriptSyntacticAndSemanticIssues: false, tsconfigRootDir: 'tests/fixtures/services', - extraFileExtensions: ['foo'], + extraFileExtensions: ['.foo'], }; parseForESLint(code, config); expect(spy).toHaveBeenCalledWith(code, { diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index e48d107de649..ecdfa9ec099e 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.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index ceb51bc56a62..ab7559eba6a5 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "2.14.0", + "version": "2.15.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 beeed3ee12db..28755f572ec7 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/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.15.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.14.0...v2.15.0) (2020-01-06) + + +### Bug Fixes + +* **typescript-estree:** correct persisted parse for windows ([#1406](https://github.com/typescript-eslint/typescript-eslint/issues/1406)) ([1a42f3d](https://github.com/typescript-eslint/typescript-eslint/commit/1a42f3d)) + + + + + # [2.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.13.0...v2.14.0) (2019-12-30) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index c20b6a390254..2c68153bfac4 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "2.14.0", + "version": "2.15.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -55,12 +55,10 @@ "@types/debug": "^4.1.5", "@types/glob": "^7.1.1", "@types/is-glob": "^4.0.1", - "@types/lodash.isplainobject": "^4.0.4", "@types/lodash.unescape": "^4.0.4", "@types/semver": "^6.2.0", "@types/tmp": "^0.1.0", - "@typescript-eslint/shared-fixtures": "2.14.0", - "lodash.isplainobject": "4.0.6", + "@typescript-eslint/shared-fixtures": "2.15.0", "tmp": "^0.1.0", "typescript": "*" }, diff --git a/packages/typescript-estree/src/create-program/createProjectProgram.ts b/packages/typescript-estree/src/create-program/createProjectProgram.ts index 9864c20cea01..32f81a40f667 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgram.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgram.ts @@ -7,9 +7,12 @@ import { ASTAndProgram } from './shared'; const log = debug('typescript-eslint:typescript-estree:createProjectProgram'); +const DEFAULT_EXTRA_FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']; + /** * @param code The code of the file being linted - * @param options The config object + * @param createDefaultProgram True if the default program should be created + * @param extra The config object * @returns If found, returns the source file corresponding to the code and the containing program */ function createProjectProgram( @@ -38,11 +41,26 @@ function createProjectProgram( ]; let hasMatchedAnError = false; + const extraFileExtensions = extra.extraFileExtensions || []; + + extraFileExtensions.forEach(extraExtension => { + if (!extraExtension.startsWith('.')) { + errorLines.push( + `Found unexpected extension "${extraExtension}" specified with the "extraFileExtensions" option. Did you mean ".${extraExtension}"?`, + ); + } + if (DEFAULT_EXTRA_FILE_EXTENSIONS.includes(extraExtension)) { + errorLines.push( + `You unnecessarily included the extension "${extraExtension}" with the "extraFileExtensions" option. This extension is already handled by the parser by default.`, + ); + } + }); + const fileExtension = path.extname(extra.filePath); - if (!['.ts', '.tsx', '.js', '.jsx'].includes(fileExtension)) { + if (!DEFAULT_EXTRA_FILE_EXTENSIONS.includes(fileExtension)) { const nonStandardExt = `The extension for the file (${fileExtension}) is non-standard`; - if (extra.extraFileExtensions && extra.extraFileExtensions.length > 0) { - if (!extra.extraFileExtensions.includes(fileExtension)) { + if (extraFileExtensions.length > 0) { + if (!extraFileExtensions.includes(fileExtension)) { errorLines.push( `${nonStandardExt}. It should be added to your existing "parserOptions.extraFileExtensions".`, ); diff --git a/packages/typescript-estree/src/create-program/shared.ts b/packages/typescript-estree/src/create-program/shared.ts index b828478073e2..8aceb204c474 100644 --- a/packages/typescript-estree/src/create-program/shared.ts +++ b/packages/typescript-estree/src/create-program/shared.ts @@ -32,7 +32,7 @@ const correctPathCasing = useCaseSensitiveFileNames function getCanonicalFileName(filePath: string): CanonicalPath { let normalized = path.normalize(filePath); - if (normalized.endsWith('/')) { + if (normalized.endsWith(path.sep)) { normalized = normalized.substr(0, normalized.length - 1); } return correctPathCasing(normalized) as CanonicalPath; diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 82755ef99079..fb62c090bb79 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -366,7 +366,7 @@ function parseAndGenerateServices( )!; /** - * Determine whatever or not two-way maps of converted AST nodes should be preserved + * Determine if two-way maps of converted AST nodes should be preserved * during the conversion process */ const shouldPreserveNodeMaps = diff --git a/packages/typescript-estree/tests/ast-alignment/parse.ts b/packages/typescript-estree/tests/ast-alignment/parse.ts index a56efd778f64..42f6e0f68fcb 100644 --- a/packages/typescript-estree/tests/ast-alignment/parse.ts +++ b/packages/typescript-estree/tests/ast-alignment/parse.ts @@ -3,7 +3,6 @@ import { ParserPlugin } from '@babel/parser'; import { codeFrameColumns } from '@babel/code-frame'; import * as parser from '../../src/parser'; -import * as parseUtils from './utils'; function createError( message: string, @@ -92,14 +91,10 @@ export function parse( try { switch (opts.parser) { case '@typescript-eslint/typescript-estree': - result.ast = parseUtils.normalizeNodeTypes( - parseWithTypeScriptESTree(text, opts.jsx), - ); + result.ast = parseWithTypeScriptESTree(text, opts.jsx); break; case '@babel/parser': - result.ast = parseUtils.normalizeNodeTypes( - parseWithBabelParser(text, opts.jsx), - ); + result.ast = parseWithBabelParser(text, opts.jsx); break; default: throw new Error( diff --git a/packages/typescript-estree/tests/ast-alignment/spec.ts b/packages/typescript-estree/tests/ast-alignment/spec.ts index 4c16a4143c8d..9e308b73c643 100644 --- a/packages/typescript-estree/tests/ast-alignment/spec.ts +++ b/packages/typescript-estree/tests/ast-alignment/spec.ts @@ -78,7 +78,7 @@ fixturesToTest.forEach(fixture => { ), ).toEqual( parseUtils.removeLocationDataAndSourceTypeFromProgramNode( - typeScriptESTreeResult.ast, + parseUtils.preprocessTypescriptAST(typeScriptESTreeResult.ast), fixture.ignoreSourceType, ), ); diff --git a/packages/typescript-estree/tests/ast-alignment/utils.ts b/packages/typescript-estree/tests/ast-alignment/utils.ts index 78b7df66fe67..4830a7f1f5a5 100644 --- a/packages/typescript-estree/tests/ast-alignment/utils.ts +++ b/packages/typescript-estree/tests/ast-alignment/utils.ts @@ -1,90 +1,25 @@ // babel types are something we don't really care about /* eslint-disable @typescript-eslint/no-explicit-any */ -import { AST_NODE_TYPES } from '../../src/ts-estree'; -import isPlainObject from 'lodash.isplainobject'; - -/** - * By default, pretty-format (within Jest matchers) retains the names/types of nodes from the babylon AST, - * quick and dirty way to avoid that is to JSON.stringify and then JSON.parser the - * ASTs before comparing them with pretty-format - * - * @param {Object} ast raw AST - * @returns {Object} normalized AST - */ -export function normalizeNodeTypes(ast: any): any { - return JSON.parse(JSON.stringify(ast)); -} - -/** - * Removes the given keys from the given AST object recursively - * @param root A JavaScript object to remove keys from - * @param keysToOmit Names and predicate functions use to determine what keys to omit from the final object - * @param nodes advance ast modifications - * @returns {Object} formatted object - */ -export function omitDeep( - root: any, - keysToOmit: { key: string; predicate: Function }[], - nodes: Record void> = {}, -): any { - function shouldOmit(keyName: string, val: any): boolean { - if (keysToOmit?.length) { - return keysToOmit.some( - keyConfig => keyConfig.key === keyName && keyConfig.predicate(val), - ); - } - return false; - } - - function visit(node: any, parent: any): void { - if (!node) { - return; - } - - for (const prop in node) { - if (Object.prototype.hasOwnProperty.call(node, prop)) { - if (shouldOmit(prop, node[prop])) { - delete node[prop]; - continue; - } - - const child = node[prop]; - - if (Array.isArray(child)) { - for (const el of child) { - visit(el, node); - } - } else if (isPlainObject(child)) { - visit(child, node); - } - } - } - - if (typeof node.type === 'string' && node.type in nodes) { - nodes[node.type](node, parent); - } - } - - visit(root, null); - return root; -} +import { AST_NODE_TYPES, TSESTree } from '../../src/ts-estree'; +import { deeplyCopy, omitDeep } from '../../tools/test-utils'; +import * as BabelTypes from '@babel/types'; /** * Common predicates for Babylon AST preprocessing */ const always = (): boolean => true; -const ifNumber = (val: any): boolean => typeof val === 'number'; +const ifNumber = (val: unknown): boolean => typeof val === 'number'; /** * - Babylon wraps the "Program" node in an extra "File" node, normalize this for simplicity for now... * - Remove "start" and "end" values from Babylon nodes to reduce unimportant noise in diffs ("loc" data will still be in * each final AST and compared). * - * @param {Object} ast raw babylon AST - * @returns {Object} processed babylon AST + * @param ast raw babylon AST + * @returns processed babylon AST */ -export function preprocessBabylonAST(ast: any): any { - return omitDeep( +export function preprocessBabylonAST(ast: BabelTypes.File): any { + return omitDeep( ast.program, [ { @@ -130,7 +65,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSCallSignatureDeclaration(node: any) { + TSCallSignatureDeclaration(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -143,7 +78,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSConstructSignatureDeclaration(node: any) { + TSConstructSignatureDeclaration(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -156,7 +91,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSFunctionType(node: any) { + TSFunctionType(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -169,7 +104,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSConstructorType(node: any) { + TSConstructorType(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -182,7 +117,7 @@ export function preprocessBabylonAST(ast: any): any { /** * Awaiting feedback on Babel issue https://github.com/babel/babel/issues/9231 */ - TSMethodSignature(node: any) { + TSMethodSignature(node) { if (node.typeAnnotation) { node.returnType = node.typeAnnotation; delete node.typeAnnotation; @@ -215,7 +150,7 @@ export function preprocessBabylonAST(ast: any): any { }; } }, - ClassProperty(node: any) { + ClassProperty(node) { /** * Babel: ClassProperty + abstract: true * ts-estree: TSAbstractClassProperty @@ -233,7 +168,7 @@ export function preprocessBabylonAST(ast: any): any { node.declare = false; } }, - TSExpressionWithTypeArguments(node: any, parent: any) { + TSExpressionWithTypeArguments(node, parent: any) { if (parent.type === 'TSInterfaceDeclaration') { node.type = 'TSInterfaceHeritage'; } else if ( @@ -266,17 +201,17 @@ export function preprocessBabylonAST(ast: any): any { * babel: sets optional property as true/undefined * ts-estree: sets optional property as true/false */ - MemberExpression(node: any) { + MemberExpression(node) { if (!node.optional) { node.optional = false; } }, - CallExpression(node: any) { + CallExpression(node) { if (!node.optional) { node.optional = false; } }, - OptionalCallExpression(node: any) { + OptionalCallExpression(node) { if (!node.optional) { node.optional = false; } @@ -286,7 +221,7 @@ export function preprocessBabylonAST(ast: any): any { * babel: sets asserts property as true/undefined * ts-estree: sets asserts property as true/false */ - TSTypePredicate(node: any) { + TSTypePredicate(node) { if (!node.asserts) { node.asserts = false; } @@ -302,9 +237,9 @@ export function preprocessBabylonAST(ast: any): any { * * See: https://github.com/babel/babel/issues/6681 * - * @param {Object} ast the raw AST with a Program node at its top level - * @param {boolean} ignoreSourceType fix for issues with unambiguous type detection - * @returns {Object} the ast with the location data removed from the Program node + * @param ast the raw AST with a Program node at its top level + * @param ignoreSourceType fix for issues with unambiguous type detection + * @returns the ast with the location data removed from the Program node */ export function removeLocationDataAndSourceTypeFromProgramNode( ast: any, @@ -317,3 +252,12 @@ export function removeLocationDataAndSourceTypeFromProgramNode( } return ast; } + +/** + * Returns a raw copy of the typescript AST + * @param ast the AST object + * @returns copy of the AST object + */ +export function preprocessTypescriptAST(ast: T): T { + return deeplyCopy(ast); +} diff --git a/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap index a73f908a3623..84f3780a21c2 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap @@ -132662,7 +132662,7 @@ Object { "pattern": "foo.", }, "type": "Literal", - "value": Object {}, + "value": /foo\\./, }, "loc": Object { "end": Object { @@ -132859,7 +132859,7 @@ Object { "pattern": "[\\\\u{0000000000000061}-\\\\u{7A}]", }, "type": "Literal", - "value": Object {}, + "value": /\\[\\\\u\\{0000000000000061\\}-\\\\u\\{7A\\}\\]/u, }, "loc": Object { "end": Object { @@ -133253,7 +133253,7 @@ Object { "pattern": "foo", }, "type": "Literal", - "value": Object {}, + "value": /foo/u, }, "loc": Object { "end": Object { @@ -133450,7 +133450,7 @@ Object { "pattern": "foo", }, "type": "Literal", - "value": Object {}, + "value": /foo/y, }, "loc": Object { "end": Object { diff --git a/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap index c09d5159cd4b..616c95d748e2 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap @@ -311,12 +311,32 @@ Object { } `; +exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is empty the extension does not match 1`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/other/unknownFileType.unknown. +The extension for the file (.unknown) is non-standard. You should add \\"parserOptions.extraFileExtensions\\" to your config." +`; + +exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty invalid extension 1`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/other/unknownFileType.unknown. +Found unexpected extension \\"unknown\\" specified with the \\"extraFileExtensions\\" option. Did you mean \\".unknown\\"? +The extension for the file (.unknown) is non-standard. It should be added to your existing \\"parserOptions.extraFileExtensions\\"." +`; + exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension does not match 1`] = ` "\\"parserOptions.project\\" has been set for @typescript-eslint/parser. The file does not match your project config: tests/fixtures/invalidFileErrors/other/unknownFileType.unknown. The extension for the file (.unknown) is non-standard. It should be added to your existing \\"parserOptions.extraFileExtensions\\"." `; +exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension matches duplicate extension 1`] = ` +"\\"parserOptions.project\\" has been set for @typescript-eslint/parser. +The file does not match your project config: tests/fixtures/invalidFileErrors/ts/notIncluded.ts. +You unnecessarily included the extension \\".ts\\" with the \\"extraFileExtensions\\" option. This extension is already handled by the parser by default. +The file must be included in at least one of the projects provided." +`; + exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension matches the file isn't included 1`] = ` "\\"parserOptions.project\\" has been set for @typescript-eslint/parser. The file does not match your project config: tests/fixtures/invalidFileErrors/other/notIncluded.vue. @@ -1283,6 +1303,224 @@ Object { } `; +exports[`parse() isolated parsing should parse .json file - without JSX content - parserOptions.jsx = false 1`] = ` +Object { + "ast": Object { + "body": Array [ + Object { + "expression": Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "properties": Array [ + Object { + "computed": false, + "key": Object { + "loc": Object { + "end": Object { + "column": 5, + "line": 1, + }, + "start": Object { + "column": 2, + "line": 1, + }, + }, + "range": Array [ + 2, + 5, + ], + "raw": "\\"x\\"", + "type": "Literal", + "value": "x", + }, + "kind": "init", + "loc": Object { + "end": Object { + "column": 8, + "line": 1, + }, + "start": Object { + "column": 2, + "line": 1, + }, + }, + "method": false, + "range": Array [ + 2, + 8, + ], + "shorthand": false, + "type": "Property", + "value": Object { + "loc": Object { + "end": Object { + "column": 8, + "line": 1, + }, + "start": Object { + "column": 7, + "line": 1, + }, + }, + "range": Array [ + 7, + 8, + ], + "raw": "1", + "type": "Literal", + "value": 1, + }, + }, + ], + "range": Array [ + 0, + 10, + ], + "type": "ObjectExpression", + }, + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 10, + ], + "type": "ExpressionStatement", + }, + ], + "comments": Array [], + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 10, + ], + "sourceType": "script", + "tokens": Array [ + Object { + "loc": Object { + "end": Object { + "column": 1, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 1, + ], + "type": "Punctuator", + "value": "{", + }, + Object { + "loc": Object { + "end": Object { + "column": 5, + "line": 1, + }, + "start": Object { + "column": 2, + "line": 1, + }, + }, + "range": Array [ + 2, + 5, + ], + "type": "String", + "value": "\\"x\\"", + }, + Object { + "loc": Object { + "end": Object { + "column": 6, + "line": 1, + }, + "start": Object { + "column": 5, + "line": 1, + }, + }, + "range": Array [ + 5, + 6, + ], + "type": "Punctuator", + "value": ":", + }, + Object { + "loc": Object { + "end": Object { + "column": 8, + "line": 1, + }, + "start": Object { + "column": 7, + "line": 1, + }, + }, + "range": Array [ + 7, + 8, + ], + "type": "Numeric", + "value": "1", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 9, + "line": 1, + }, + }, + "range": Array [ + 9, + 10, + ], + "type": "Punctuator", + "value": "}", + }, + ], + "type": "Program", + }, + "services": Object { + "esTreeNodeToTSNodeMap": undefined, + "program": undefined, + "tsNodeToESTreeNodeMap": undefined, + }, +} +`; + exports[`parse() isolated parsing should parse .jsx file - with JSX content - parserOptions.jsx = false 1`] = ` Object { "ast": Object { diff --git a/packages/typescript-estree/tests/lib/parse.ts b/packages/typescript-estree/tests/lib/parse.ts index 9f469f4405db..6cba9ba867b0 100644 --- a/packages/typescript-estree/tests/lib/parse.ts +++ b/packages/typescript-estree/tests/lib/parse.ts @@ -258,12 +258,17 @@ describe('parse()', () => { jsxSetting, shouldThrow = false, }: { - ext: '.js' | '.jsx' | '.ts' | '.tsx' | '.vue'; + ext: '.js' | '.jsx' | '.ts' | '.tsx' | '.vue' | '.json'; jsxContent: boolean; jsxSetting: boolean; shouldThrow?: boolean; }): void => { - const code = jsxContent ? 'const x =
;' : 'const x = 1'; + const code = + ext === '.json' + ? '{ "x": 1 }' + : jsxContent + ? 'const x =
;' + : 'const x = 1'; it(`should parse ${ext} file - ${ jsxContent ? 'with' : 'without' } JSX content - parserOptions.jsx = ${jsxSetting}`, () => { @@ -394,6 +399,11 @@ describe('parse()', () => { jsxContent: true, jsxSetting: true, }); + testParse({ + ext: '.json', + jsxContent: false, + jsxSetting: false, + }); }); describe('invalid file error messages', () => { @@ -406,13 +416,24 @@ describe('parse()', () => { loc: true, tsconfigRootDir: PROJECT_DIR, project: './tsconfig.json', - extraFileExtensions: ['.vue'], }; - const testParse = (filePath: string) => (): void => { - parser.parseAndGenerateServices(code, { - ...config, - filePath: join(PROJECT_DIR, filePath), - }); + const testParse = ( + filePath: string, + extraFileExtensions: string[] = ['.vue'], + ) => (): void => { + try { + parser.parseAndGenerateServices(code, { + ...config, + extraFileExtensions, + filePath: join(PROJECT_DIR, filePath), + }); + } catch (error) { + /** + * Aligns paths between environments, node for windows uses `\`, for linux and mac uses `/` + */ + error.message = error.message.replace(/\\(?!["])/gm, '/'); + throw error; + } }; describe('project includes', () => { @@ -431,6 +452,18 @@ describe('parse()', () => { }); }); + describe('"parserOptions.extraFileExtensions" is empty', () => { + it('should not error', () => { + expect(testParse('ts/included.ts', [])).not.toThrow(); + }); + + it('the extension does not match', () => { + expect( + testParse('other/unknownFileType.unknown', []), + ).toThrowErrorMatchingSnapshot(); + }); + }); + describe('"parserOptions.extraFileExtensions" is non-empty', () => { describe('the extension matches', () => { it('the file is included', () => { @@ -442,6 +475,18 @@ describe('parse()', () => { testParse('other/notIncluded.vue'), ).toThrowErrorMatchingSnapshot(); }); + + it('duplicate extension', () => { + expect( + testParse('ts/notIncluded.ts', ['.ts']), + ).toThrowErrorMatchingSnapshot(); + }); + }); + + it('invalid extension', () => { + expect( + testParse('other/unknownFileType.unknown', ['unknown']), + ).toThrowErrorMatchingSnapshot(); }); it('the extension does not match', () => { diff --git a/packages/typescript-estree/tools/test-utils.ts b/packages/typescript-estree/tools/test-utils.ts index b9fcb51cab64..2a9b2f11ddfa 100644 --- a/packages/typescript-estree/tools/test-utils.ts +++ b/packages/typescript-estree/tools/test-utils.ts @@ -1,22 +1,6 @@ import * as parser from '../src/parser'; import { TSESTreeOptions } from '../src/parser-options'; -/** - * Returns a raw copy of the given AST - * @param {Object} ast the AST object - * @returns {Object} copy of the AST object - */ -export function getRaw(ast: parser.TSESTree.Program): parser.TSESTree.Program { - return JSON.parse( - JSON.stringify(ast, (key, value) => { - if ((key === 'start' || key === 'end') && typeof value === 'number') { - return undefined; - } - return value; - }), - ); -} - export function parseCodeAndGenerateServices( code: string, config: TSESTreeOptions, @@ -27,24 +11,24 @@ export function parseCodeAndGenerateServices( /** * Returns a function which can be used as the callback of a Jest test() block, * and which performs an assertion on the snapshot for the given code and config. - * @param {string} code The source code to parse - * @param {TSESTreeOptions} config the parser configuration - * @param {boolean} generateServices Flag determining whether to generate ast maps and program or not - * @returns {jest.ProvidesCallback} callback for Jest it() block + * @param code The source code to parse + * @param config the parser configuration + * @param generateServices Flag determining whether to generate ast maps and program or not + * @returns callback for Jest it() block */ export function createSnapshotTestBlock( code: string, config: TSESTreeOptions, generateServices?: true, -): () => void { +): jest.ProvidesCallback { /** - * @returns {Object} the AST object + * @returns the AST object */ function parse(): parser.TSESTree.Program { const ast = generateServices ? parser.parseAndGenerateServices(code, config).ast : parser.parse(code, config); - return getRaw(ast); + return deeplyCopy(ast); } return (): void => { @@ -84,3 +68,84 @@ export function isJSXFileType(fileType: string): boolean { } return fileType === 'js' || fileType === 'jsx' || fileType === 'tsx'; } + +/** + * Returns a raw copy of the typescript AST + * @param ast the AST object + * @returns copy of the AST object + */ +export function deeplyCopy(ast: T): T { + return omitDeep(ast) as T; +} + +type UnknownObject = Record; + +function isObjectLike(value: unknown | null): value is UnknownObject { + return ( + typeof value === 'object' && !(value instanceof RegExp) && value !== null + ); +} + +/** + * Removes the given keys from the given AST object recursively + * @param root A JavaScript object to remove keys from + * @param keysToOmit Names and predicate functions use to determine what keys to omit from the final object + * @param selectors advance ast modifications + * @returns formatted object + */ +export function omitDeep( + root: T, + keysToOmit: { key: string; predicate: (value: unknown) => boolean }[] = [], + selectors: Record< + string, + (node: UnknownObject, parent: UnknownObject | null) => void + > = {}, +): UnknownObject { + function shouldOmit(keyName: string, val: unknown): boolean { + if (keysToOmit?.length) { + return keysToOmit.some( + keyConfig => keyConfig.key === keyName && keyConfig.predicate(val), + ); + } + return false; + } + + function visit( + oNode: UnknownObject, + parent: UnknownObject | null, + ): UnknownObject { + if (!Array.isArray(oNode) && !isObjectLike(oNode)) { + return oNode; + } + + const node = { ...oNode }; + + for (const prop in node) { + if (Object.prototype.hasOwnProperty.call(node, prop)) { + if (shouldOmit(prop, node[prop]) || typeof node[prop] === 'undefined') { + delete node[prop]; + continue; + } + + const child = node[prop]; + if (Array.isArray(child)) { + const value = []; + for (const el of child) { + value.push(visit(el, node)); + } + node[prop] = value; + } else if (isObjectLike(child)) { + node[prop] = visit(child, node); + } + } + } + + if (typeof node.type === 'string' && node.type in selectors) { + selectors[node.type](node, parent); + } + + return node; + } + + return visit(root as UnknownObject, null); +} diff --git a/yarn.lock b/yarn.lock index 8ce1cf0d491b..56703239ab25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1390,13 +1390,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== -"@types/lodash.isplainobject@^4.0.4": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#757d2dcdecbb32f4452018b285a586776092efd1" - integrity sha512-8G41YFhmOl8Ck6NrwLK5hhnbz6ADfuDJP+zusDnX3PoYhfC60+H/rQE6zmdO4yFzPCPJPY4oGZK2spbXm6gYEA== - dependencies: - "@types/lodash" "*" - "@types/lodash.memoize@^4.1.4": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.6.tgz#3221f981790a415cab1a239f25c17efd8b604c23" @@ -5487,11 +5480,6 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= -lodash.isplainobject@4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - lodash.map@^4.5.1: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"