diff --git a/.cspell.json b/.cspell.json index 6133a8917ab4..c6623037c42c 100644 --- a/.cspell.json +++ b/.cspell.json @@ -107,6 +107,7 @@ "preact", "Premade", "prettier's", + "quasis", "Quickstart", "recurse", "redeclaration", diff --git a/CHANGELOG.md b/CHANGELOG.md index 974127de56a7..af44283abf4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +## 6.19.0 (2024-01-15) + + +### 🚀 Features + +- **eslint-plugin:** [prefer-promise-reject-errors] add rule ([#8011](https://github.com/typescript-eslint/typescript-eslint/pull/8011)) +- **eslint-plugin:** [no-array-delete] add new rule ([#8067](https://github.com/typescript-eslint/typescript-eslint/pull/8067)) +- **eslint-plugin:** [no-useless-template-literals] add fix suggestions ([#8065](https://github.com/typescript-eslint/typescript-eslint/pull/8065)) + +### 🩹 Fixes + +- **eslint-plugin:** [no-unnecessary-type-assertion] detect unnecessary non-null-assertion on a call expression ([#8143](https://github.com/typescript-eslint/typescript-eslint/pull/8143)) +- **eslint-plugin:** [no-unnecesary-type-assertion] treat unknown/any as nullable ([#8089](https://github.com/typescript-eslint/typescript-eslint/pull/8089)) +- **typescript-estree:** add JSDocParsingMode enum merge for typescript/lib/tsserverlibrary ([#8193](https://github.com/typescript-eslint/typescript-eslint/pull/8193)) +- **typescript-estree:** disallow `using` as the variable keyword for `for..in` loops ([#7649](https://github.com/typescript-eslint/typescript-eslint/pull/7649)) +- **typescript-estree:** fix incorrect backwards-compat augmentation in TS 5.3 ([#8181](https://github.com/typescript-eslint/typescript-eslint/pull/8181)) + +### ❤️ Thank You + +- auvred @auvred +- Brad Zacher @bradzacher +- Josh Goldberg ✨ +- Joshua Chen +- LJX @lvjiaxuan +- Steven @Solo-steven +- StyleShit @StyleShit + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) diff --git a/docs/contributing/local-development/Local_Linking.mdx b/docs/contributing/local-development/Local_Linking.mdx index 00908758485c..7c7b6cdaf086 100644 --- a/docs/contributing/local-development/Local_Linking.mdx +++ b/docs/contributing/local-development/Local_Linking.mdx @@ -8,6 +8,7 @@ The general strategy to do so is: 1. [Global linking](#global-linking): Use your package manager's global link command to make `@typescript-eslint/*` packages available as global symlinks. 2. [Repository linking](#repository-linking): Use your package manager's link command to reference those global symlinks in the local downstream repository. +3. [Trying rules](#trying-rules): Test your local rules and plugins by enabling them in the local downstream repository. ## Global Linking @@ -45,6 +46,27 @@ Now, you should be able to run ESLint in the local downstream repository as you To check that the local package is being used, consider adding a `console.log("Hello, world!");` to a file such as `./packages/eslint-plugin/dist/index.js` and making sure that log appears when linting the local downstream repository. ::: +## Trying Rules + +Now that you've linked the `@typescript-eslint/*` packages with your local downstream repository, the next step would be to include the rule on the local repository ESLint configuration file, e.g: + +```json title=".eslintrc.json" +{ + "rules": { + "@typescript-eslint/your-awesome-rule": "error" + } + // ... +} +``` + +After that, you need to run your repository's `lint` script and your changes should be reflected on the project. + +:::note +Changes to `@typescript-eslint/` packages will not be reflected inside your linked repository until they're built locally. +To re-build all packages, run `yarn build` from the root. +To start a watch mode builder on just the ESLint plugin, run `yarn build --watch` from `./packages/eslint-plugin`. +::: + ## Troubleshooting ### Packages Not Found (`Cannot find module`) diff --git a/nx.json b/nx.json index 552b09332128..d85a0833983e 100644 --- a/nx.json +++ b/nx.json @@ -38,11 +38,16 @@ "cache": true }, "lint": { + "dependsOn": ["eslint-plugin:build"], "inputs": [ "default", "{workspaceRoot}/.eslintrc.js", "{workspaceRoot}/yarn.lock", - "{workspaceRoot}/.eslintignore" + "{workspaceRoot}/.eslintignore", + { + "dependentTasksOutputFiles": "**/*.js", + "transitive": false + } ], "cache": true }, diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md index 9608d2aefe36..47d00a853303 100644 --- a/packages/ast-spec/CHANGELOG.md +++ b/packages/ast-spec/CHANGELOG.md @@ -1,3 +1,23 @@ +## 6.19.0 (2024-01-15) + + +### 🩹 Fixes + +- **typescript-estree:** disallow `using` as the variable keyword for `for..in` loops + + +### ❤️ Thank You + +- auvred +- Brad Zacher +- Josh Goldberg ✨ +- Joshua Chen +- LJX +- Steven +- StyleShit + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json index 3e396707de5f..1132cccd79de 100644 --- a/packages/ast-spec/package.json +++ b/packages/ast-spec/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/ast-spec", - "version": "6.18.1", + "version": "6.19.0", "description": "Complete specification for the TypeScript-ESTree AST", "private": true, "keywords": [ diff --git a/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/fixture.ts b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/fixture.ts new file mode 100644 index 000000000000..b65a9983c6cd --- /dev/null +++ b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/fixture.ts @@ -0,0 +1 @@ +for(using foo in {}); \ No newline at end of file diff --git a/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/1-TSESTree-Error.shot new file mode 100644 index 000000000000..f914c11ac282 --- /dev/null +++ b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures statement ForInStatement _error_ using-initializer TSESTree - Error 1`] = ` +"TSError +> 1 | for(using foo in {}); + | ^^^^^^^^^ The left-hand side of a 'for...in' statement cannot be a 'using' declaration." +`; diff --git a/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/2-Babel-Error.shot new file mode 100644 index 000000000000..f1c91950c6ec --- /dev/null +++ b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures statement ForInStatement _error_ using-initializer Babel - Error 1`] = `[SyntaxError: For-in loop may not start with 'using' declaration. (1:4)]`; diff --git a/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/3-Alignment-Error.shot new file mode 100644 index 000000000000..3987fd760265 --- /dev/null +++ b/packages/ast-spec/src/statement/ForInStatement/fixtures/_error_/using-initializer/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures statement ForInStatement _error_ using-initializer Error Alignment 1`] = `"Both errored"`; diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index b49de9be6a19..3633512769c8 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for eslint-plugin-internal to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for eslint-plugin-internal to align it with other projects, there were no code changes. diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index e0299ce75772..33a7edcb37f6 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": "6.18.1", + "version": "6.19.0", "private": true, "main": "dist/index.js", "scripts": { @@ -14,10 +14,10 @@ }, "dependencies": { "@prettier/sync": "*", - "@typescript-eslint/rule-tester": "6.18.1", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/type-utils": "6.18.1", - "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/rule-tester": "6.19.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "prettier": "^3.0.3" }, "devDependencies": { diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index c98f9a8d0e66..44fcbf80c54b 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for eslint-plugin-tslint to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for eslint-plugin-tslint to align it with other projects, there were no code changes. diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index 36ba58ba2895..ca06480892b5 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": "6.18.1", + "version": "6.19.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "ESLint plugin that wraps a TSLint configuration and lints the whole source using TSLint", @@ -46,7 +46,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/utils": "6.18.1" + "@typescript-eslint/utils": "6.19.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0", @@ -55,7 +55,7 @@ }, "devDependencies": { "@types/lodash": "*", - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/parser": "6.19.0", "jest": "29.7.0", "prettier": "^3.0.3", "rimraf": "*" diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 88fb389496c9..64a4e1dcd452 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,3 +1,34 @@ +## 6.19.0 (2024-01-15) + + +### 🚀 Features + +- **eslint-plugin:** [prefer-promise-reject-errors] add rule + +- **eslint-plugin:** [no-array-delete] add new rule + +- **eslint-plugin:** [no-useless-template-literals] add fix suggestions + + +### 🩹 Fixes + +- **eslint-plugin:** [no-unnecessary-type-assertion] detect unnecessary non-null-assertion on a call expression + +- **eslint-plugin:** [no-unnecesary-type-assertion] treat unknown/any as nullable + + +### ❤️ Thank You + +- auvred +- Brad Zacher +- Josh Goldberg ✨ +- Joshua Chen +- LJX +- Steven +- StyleShit + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) diff --git a/packages/eslint-plugin/docs/rules/no-array-delete.md b/packages/eslint-plugin/docs/rules/no-array-delete.md new file mode 100644 index 000000000000..3abbbc6ececc --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-array-delete.md @@ -0,0 +1,40 @@ +--- +description: 'Disallow using the `delete` operator on array values.' +--- + +> 🛑 This file is source code, not the primary documentation location! 🛑 +> +> See **https://typescript-eslint.io/rules/no-array-delete** for documentation. + +When using the `delete` operator with an array value, the array's `length` property is not affected, +but the element at the specified index is removed and leaves an empty slot in the array. +This is likely to lead to unexpected behavior. As mentioned in the +[MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#deleting_array_elements), +the recommended way to remove an element from an array is by using the +[`Array#splice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) method. + +## Examples + + + +### ❌ Incorrect + +```ts +declare const arr: number[]; + +delete arr[0]; +``` + +### ✅ Correct + +```ts +declare const arr: number[]; + +arr.splice(0, 1); +``` + + + +## When Not To Use It + +When you want to allow the delete operator with array expressions. diff --git a/packages/eslint-plugin/docs/rules/no-floating-promises.md b/packages/eslint-plugin/docs/rules/no-floating-promises.md index e137a85aaf97..da46d7f98d18 100644 --- a/packages/eslint-plugin/docs/rules/no-floating-promises.md +++ b/packages/eslint-plugin/docs/rules/no-floating-promises.md @@ -98,8 +98,9 @@ This allows you to skip checking of async IIFEs (Immediately Invoked function Ex Examples of **correct** code for this rule with `{ ignoreIIFE: true }`: + ```ts option='{ "ignoreIIFE": true }' showPlaygroundButton -await(async function () { +await (async function () { await res(1); })(); diff --git a/packages/eslint-plugin/docs/rules/prefer-promise-reject-errors.md b/packages/eslint-plugin/docs/rules/prefer-promise-reject-errors.md new file mode 100644 index 000000000000..28e465cf00f1 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/prefer-promise-reject-errors.md @@ -0,0 +1,50 @@ +--- +description: 'Require using Error objects as Promise rejection reasons.' +--- + +> 🛑 This file is source code, not the primary documentation location! 🛑 +> +> See **https://typescript-eslint.io/rules/prefer-promise-reject-errors** for documentation. + +This rule extends the base [`eslint/prefer-promise-reject-errors`](https://eslint.org/docs/rules/prefer-promise-reject-errors) rule. +It uses type information to enforce that `Promise`s are only rejected with `Error` objects. + +## Examples + + + +### ❌ Incorrect + +```ts +Promise.reject('error'); + +const err = new Error(); +Promise.reject('an ' + err); + +new Promise((resolve, reject) => reject('error')); + +new Promise((resolve, reject) => { + const err = new Error(); + reject('an ' + err); +}); +``` + +### ✅ Correct + +```ts +Promise.reject(new Error()); + +class CustomError extends Error { + // ... +} +Promise.reject(new CustomError()); + +new Promise((resolve, reject) => reject(new Error())); + +new Promise((resolve, reject) => { + class CustomError extends Error { + // ... + } + return reject(new CustomError()); +}); +``` diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 07733150bbdf..2066c7e99c29 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "6.18.1", + "version": "6.19.0", "description": "TypeScript plugin for ESLint", "files": [ "dist", @@ -57,10 +57,10 @@ }, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/type-utils": "6.18.1", - "@typescript-eslint/utils": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -73,8 +73,8 @@ "@types/debug": "*", "@types/marked": "*", "@types/natural-compare": "*", - "@typescript-eslint/rule-schema-to-typescript-types": "6.18.1", - "@typescript-eslint/rule-tester": "6.18.1", + "@typescript-eslint/rule-schema-to-typescript-types": "6.19.0", + "@typescript-eslint/rule-tester": "6.19.0", "ajv": "^6.12.6", "chalk": "^5.3.0", "cross-fetch": "*", diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 9881e3397de2..3a5e6343b197 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -39,6 +39,7 @@ export = { '@typescript-eslint/naming-convention': 'error', 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-array-delete': 'error', '@typescript-eslint/no-base-to-string': 'error', '@typescript-eslint/no-confusing-non-null-assertion': 'error', '@typescript-eslint/no-confusing-void-expression': 'error', @@ -125,6 +126,8 @@ export = { '@typescript-eslint/prefer-namespace-keyword': 'error', '@typescript-eslint/prefer-nullish-coalescing': 'error', '@typescript-eslint/prefer-optional-chain': 'error', + 'prefer-promise-reject-errors': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'error', '@typescript-eslint/prefer-readonly': 'error', '@typescript-eslint/prefer-readonly-parameter-types': 'error', '@typescript-eslint/prefer-reduce-type-parameter': 'error', diff --git a/packages/eslint-plugin/src/configs/disable-type-checked.ts b/packages/eslint-plugin/src/configs/disable-type-checked.ts index 2fe413146c7b..4cd82bf2414e 100644 --- a/packages/eslint-plugin/src/configs/disable-type-checked.ts +++ b/packages/eslint-plugin/src/configs/disable-type-checked.ts @@ -12,6 +12,7 @@ export = { '@typescript-eslint/consistent-type-exports': 'off', '@typescript-eslint/dot-notation': 'off', '@typescript-eslint/naming-convention': 'off', + '@typescript-eslint/no-array-delete': 'off', '@typescript-eslint/no-base-to-string': 'off', '@typescript-eslint/no-confusing-void-expression': 'off', '@typescript-eslint/no-duplicate-type-constituents': 'off', @@ -41,6 +42,7 @@ export = { '@typescript-eslint/prefer-includes': 'off', '@typescript-eslint/prefer-nullish-coalescing': 'off', '@typescript-eslint/prefer-optional-chain': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'off', '@typescript-eslint/prefer-readonly': 'off', '@typescript-eslint/prefer-readonly-parameter-types': 'off', '@typescript-eslint/prefer-reduce-type-parameter': 'off', diff --git a/packages/eslint-plugin/src/configs/strict-type-checked.ts b/packages/eslint-plugin/src/configs/strict-type-checked.ts index 471175b9bba7..a0f82563b1f3 100644 --- a/packages/eslint-plugin/src/configs/strict-type-checked.ts +++ b/packages/eslint-plugin/src/configs/strict-type-checked.ts @@ -13,6 +13,7 @@ export = { '@typescript-eslint/ban-types': 'error', 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-array-delete': 'error', '@typescript-eslint/no-base-to-string': 'error', '@typescript-eslint/no-confusing-void-expression': 'error', '@typescript-eslint/no-duplicate-enum-values': 'error', @@ -61,6 +62,8 @@ export = { '@typescript-eslint/prefer-as-const': 'error', '@typescript-eslint/prefer-includes': 'error', '@typescript-eslint/prefer-literal-enum-member': 'error', + 'prefer-promise-reject-errors': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'error', '@typescript-eslint/prefer-reduce-type-parameter': 'error', '@typescript-eslint/prefer-return-this-type': 'error', '@typescript-eslint/prefer-ts-expect-error': 'error', diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 14c171af990e..94cce184242d 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -34,6 +34,7 @@ import memberOrdering from './member-ordering'; import methodSignatureStyle from './method-signature-style'; import namingConvention from './naming-convention'; import noArrayConstructor from './no-array-constructor'; +import noArrayDelete from './no-array-delete'; import noBaseToString from './no-base-to-string'; import confusingNonNullAssertionLikeNotEqual from './no-confusing-non-null-assertion'; import noConfusingVoidExpression from './no-confusing-void-expression'; @@ -109,6 +110,7 @@ import preferLiteralEnumMember from './prefer-literal-enum-member'; import preferNamespaceKeyword from './prefer-namespace-keyword'; import preferNullishCoalescing from './prefer-nullish-coalescing'; import preferOptionalChain from './prefer-optional-chain'; +import preferPromiseRejectErrors from './prefer-promise-reject-errors'; import preferReadonly from './prefer-readonly'; import preferReadonlyParameterTypes from './prefer-readonly-parameter-types'; import preferReduceTypeParameter from './prefer-reduce-type-parameter'; @@ -173,6 +175,7 @@ export default { 'method-signature-style': methodSignatureStyle, 'naming-convention': namingConvention, 'no-array-constructor': noArrayConstructor, + 'no-array-delete': noArrayDelete, 'no-base-to-string': noBaseToString, 'no-confusing-non-null-assertion': confusingNonNullAssertionLikeNotEqual, 'no-confusing-void-expression': noConfusingVoidExpression, @@ -248,6 +251,7 @@ export default { 'prefer-namespace-keyword': preferNamespaceKeyword, 'prefer-nullish-coalescing': preferNullishCoalescing, 'prefer-optional-chain': preferOptionalChain, + 'prefer-promise-reject-errors': preferPromiseRejectErrors, 'prefer-readonly': preferReadonly, 'prefer-readonly-parameter-types': preferReadonlyParameterTypes, 'prefer-reduce-type-parameter': preferReduceTypeParameter, diff --git a/packages/eslint-plugin/src/rules/no-array-delete.ts b/packages/eslint-plugin/src/rules/no-array-delete.ts new file mode 100644 index 000000000000..141332e06382 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-array-delete.ts @@ -0,0 +1,112 @@ +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; +import { getSourceCode } from '@typescript-eslint/utils/eslint-utils'; +import type * as ts from 'typescript'; + +import { + createRule, + getConstrainedTypeAtLocation, + getParserServices, +} from '../util'; + +type MessageId = 'noArrayDelete' | 'useSplice'; + +export default createRule<[], MessageId>({ + name: 'no-array-delete', + meta: { + hasSuggestions: true, + type: 'problem', + docs: { + description: 'Disallow using the `delete` operator on array values', + recommended: 'strict', + requiresTypeChecking: true, + }, + messages: { + noArrayDelete: + 'Using the `delete` operator with an array expression is unsafe.', + useSplice: 'Use `array.splice()` instead.', + }, + schema: [], + }, + defaultOptions: [], + create(context) { + const services = getParserServices(context); + const checker = services.program.getTypeChecker(); + + function isUnderlyingTypeArray(type: ts.Type): boolean { + const predicate = (t: ts.Type): boolean => + checker.isArrayType(t) || checker.isTupleType(t); + + if (type.isUnion()) { + return type.types.every(predicate); + } + + if (type.isIntersection()) { + return type.types.some(predicate); + } + + return predicate(type); + } + + return { + 'UnaryExpression[operator="delete"]'( + node: TSESTree.UnaryExpression, + ): void { + const { argument } = node; + + if (argument.type !== AST_NODE_TYPES.MemberExpression) { + return; + } + + const type = getConstrainedTypeAtLocation(services, argument.object); + + if (!isUnderlyingTypeArray(type)) { + return; + } + + context.report({ + node, + messageId: 'noArrayDelete', + suggest: [ + { + messageId: 'useSplice', + fix(fixer): TSESLint.RuleFix | null { + const { object, property } = argument; + + const shouldHaveParentheses = + property.type === AST_NODE_TYPES.SequenceExpression; + + const nodeMap = services.esTreeNodeToTSNodeMap; + const target = nodeMap.get(object).getText(); + const rawKey = nodeMap.get(property).getText(); + const key = shouldHaveParentheses ? `(${rawKey})` : rawKey; + + let suggestion = `${target}.splice(${key}, 1)`; + + const sourceCode = getSourceCode(context); + const comments = sourceCode.getCommentsInside(node); + + if (comments.length > 0) { + const indentationCount = node.loc.start.column; + const indentation = ' '.repeat(indentationCount); + + const commentsText = comments + .map(comment => { + return comment.type === AST_TOKEN_TYPES.Line + ? `//${comment.value}` + : `/*${comment.value}*/`; + }) + .join(`\n${indentation}`); + + suggestion = `${commentsText}\n${indentation}${suggestion}`; + } + + return fixer.replaceText(node, suggestion); + }, + }, + ], + }); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/src/rules/no-throw-literal.ts b/packages/eslint-plugin/src/rules/no-throw-literal.ts index f1129c252036..f3f5937c7379 100644 --- a/packages/eslint-plugin/src/rules/no-throw-literal.ts +++ b/packages/eslint-plugin/src/rules/no-throw-literal.ts @@ -5,6 +5,7 @@ import * as ts from 'typescript'; import { createRule, getParserServices, + isErrorLike, isTypeAnyType, isTypeUnknownType, } from '../util'; @@ -55,41 +56,6 @@ export default createRule({ ], create(context, [options]) { const services = getParserServices(context); - const checker = services.program.getTypeChecker(); - - function isErrorLike(type: ts.Type): boolean { - if (type.isIntersection()) { - return type.types.some(isErrorLike); - } - if (type.isUnion()) { - return type.types.every(isErrorLike); - } - - const symbol = type.getSymbol(); - if (!symbol) { - return false; - } - - if (symbol.getName() === 'Error') { - const declarations = symbol.getDeclarations() ?? []; - for (const declaration of declarations) { - const sourceFile = declaration.getSourceFile(); - if (services.program.isSourceFileDefaultLibrary(sourceFile)) { - return true; - } - } - } - - if (symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) { - for (const baseType of checker.getBaseTypes(type as ts.InterfaceType)) { - if (isErrorLike(baseType)) { - return true; - } - } - } - - return false; - } function checkThrowArgument(node: TSESTree.Node): void { if ( @@ -114,7 +80,7 @@ export default createRule({ return; } - if (isErrorLike(type)) { + if (isErrorLike(services.program, type)) { return; } diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 12a42398b3ee..0a31535797d7 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -436,7 +436,7 @@ export default createRule({ function checkCallExpression(node: TSESTree.CallExpression): void { // If this is something like arr.filter(x => /*condition*/), check `condition` if (isArrayPredicateFunction(node) && node.arguments.length) { - const callback = node.arguments[0]!; + const callback = node.arguments[0]; // Inline defined functions if ( callback.type === AST_NODE_TYPES.ArrowFunctionExpression || @@ -530,7 +530,7 @@ export default createRule({ propertyType.value.toString(), ); if (propType) { - return isNullableType(propType, { allowUndefined: true }); + return isNullableType(propType); } } const typeName = getTypeName(checker, propertyType); @@ -568,14 +568,12 @@ export default createRule({ ); if (propType) { - return isNullableType(propType, { allowUndefined: true }); + return isNullableType(propType); } return !!checker.getIndexInfoOfType(type, ts.IndexKind.String); }); - return ( - !isOwnNullable && isNullableType(prevType, { allowUndefined: true }) - ); + return !isOwnNullable && isNullableType(prevType); } return false; } @@ -612,8 +610,7 @@ export default createRule({ const possiblyVoid = isTypeFlagSet(type, ts.TypeFlags.Void); return ( isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown) || - (isOwnNullable && - (isNullableType(type, { allowUndefined: true }) || possiblyVoid)) + (isOwnNullable && (isNullableType(type) || possiblyVoid)) ); } 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 96de86efe116..58ef344bf515 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -169,7 +169,10 @@ export default createRule({ const type = getConstrainedTypeAtLocation(services, node.expression); if (!isNullableType(type)) { - if (isPossiblyUsedBeforeAssigned(node.expression)) { + if ( + node.expression.type === AST_NODE_TYPES.Identifier && + isPossiblyUsedBeforeAssigned(node.expression) + ) { return; } @@ -177,10 +180,7 @@ export default createRule({ node, messageId: 'unnecessaryAssertion', fix(fixer) { - return fixer.removeRange([ - node.expression.range[1], - node.range[1], - ]); + return fixer.removeRange([node.range[1] - 1, node.range[1]]); }, }); } else { @@ -274,7 +274,10 @@ export default createRule({ : null; } return fixer.removeRange([ - node.expression.range[1] + 1, + node.expression.range[1] + + (node.expression.type === AST_NODE_TYPES.CallExpression + ? 0 + : 1), node.range[1], ]); }, diff --git a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts index 48c714edb90a..d5a8a602b6b4 100644 --- a/packages/eslint-plugin/src/rules/no-useless-template-literals.ts +++ b/packages/eslint-plugin/src/rules/no-useless-template-literals.ts @@ -1,4 +1,4 @@ -import type { TSESTree } from '@typescript-eslint/utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -15,7 +15,8 @@ type MessageId = 'noUselessTemplateLiteral'; export default createRule<[], MessageId>({ name: 'no-useless-template-literals', meta: { - type: 'problem', + fixable: 'code', + type: 'suggestion', docs: { description: 'Disallow unnecessary template literals', recommended: 'strict', @@ -68,6 +69,22 @@ export default createRule<[], MessageId>({ context.report({ node: node.expressions[0], messageId: 'noUselessTemplateLiteral', + fix(fixer): TSESLint.RuleFix[] { + const [prevQuasi, nextQuasi] = node.quasis; + + // Remove the quasis and backticks. + return [ + fixer.removeRange([ + prevQuasi.range[1] - 3, + node.expressions[0].range[0], + ]), + + fixer.removeRange([ + node.expressions[0].range[1], + nextQuasi.range[0] + 2, + ]), + ]; + }, }); return; @@ -83,6 +100,40 @@ export default createRule<[], MessageId>({ context.report({ node: expression, messageId: 'noUselessTemplateLiteral', + fix(fixer): TSESLint.RuleFix[] { + const index = node.expressions.indexOf(expression); + const prevQuasi = node.quasis[index]; + const nextQuasi = node.quasis[index + 1]; + + // Remove the quasis' parts that are related to the current expression. + const fixes = [ + fixer.removeRange([ + prevQuasi.range[1] - 2, + expression.range[0], + ]), + + fixer.removeRange([ + expression.range[1], + nextQuasi.range[0] + 1, + ]), + ]; + + // Remove quotes for string literals (i.e. `'a'` will become `a`). + const isStringLiteral = + isUnderlyingTypeString(expression) && + expression.type === AST_NODE_TYPES.Literal; + + if (isStringLiteral) { + const escapedValue = expression.value.replace( + /([`$\\])/g, + '\\$1', + ); + + fixes.push(fixer.replaceText(expression, escapedValue)); + } + + return fixes; + }, }); }); }, diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index be498cb94ad0..c688d75d2771 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -309,8 +309,7 @@ export default createRule({ ): void { const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); const type = checker.getTypeAtLocation(tsNode.left); - const isNullish = isNullableType(type, { allowUndefined: true }); - if (!isNullish) { + if (!isNullableType(type)) { return; } diff --git a/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts b/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts new file mode 100644 index 000000000000..69494823e023 --- /dev/null +++ b/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts @@ -0,0 +1,153 @@ +import type { TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import { getDeclaredVariables } from '@typescript-eslint/utils/eslint-utils'; + +import { + createRule, + getParserServices, + isErrorLike, + isFunction, + isIdentifier, + isPromiseConstructorLike, + isPromiseLike, + isReadonlyErrorLike, +} from '../util'; + +export type MessageIds = 'rejectAnError'; + +export type Options = [ + { + allowEmptyReject?: boolean; + }, +]; + +export default createRule({ + name: 'prefer-promise-reject-errors', + meta: { + type: 'suggestion', + docs: { + description: 'Require using Error objects as Promise rejection reasons', + recommended: 'strict', + extendsBaseRule: true, + requiresTypeChecking: true, + }, + schema: [ + { + type: 'object', + properties: { + allowEmptyReject: { + type: 'boolean', + }, + }, + additionalProperties: false, + }, + ], + messages: { + rejectAnError: 'Expected the Promise rejection reason to be an Error.', + }, + }, + defaultOptions: [ + { + allowEmptyReject: false, + }, + ], + create(context, [options]) { + const services = getParserServices(context); + + function checkRejectCall(callExpression: TSESTree.CallExpression): void { + const argument = callExpression.arguments.at(0); + if (argument) { + const type = services.getTypeAtLocation(argument); + if ( + isErrorLike(services.program, type) || + isReadonlyErrorLike(services.program, type) + ) { + return; + } + } else if (options.allowEmptyReject) { + return; + } + + context.report({ + node: callExpression, + messageId: 'rejectAnError', + }); + } + + function skipChainExpression( + node: T, + ): T | TSESTree.ChainElement { + return node.type === AST_NODE_TYPES.ChainExpression + ? node.expression + : node; + } + + function typeAtLocationIsLikePromise(node: TSESTree.Node): boolean { + const type = services.getTypeAtLocation(node); + return ( + isPromiseConstructorLike(services.program, type) || + isPromiseLike(services.program, type) + ); + } + + return { + CallExpression(node): void { + const callee = skipChainExpression(node.callee); + + if (callee.type !== AST_NODE_TYPES.MemberExpression) { + return; + } + + const rejectMethodCalled = callee.computed + ? callee.property.type === AST_NODE_TYPES.Literal && + callee.property.value === 'reject' + : callee.property.name === 'reject'; + + if ( + !rejectMethodCalled || + !typeAtLocationIsLikePromise(callee.object) + ) { + return; + } + + checkRejectCall(node); + }, + NewExpression(node): void { + const callee = skipChainExpression(node.callee); + if ( + !isPromiseConstructorLike( + services.program, + services.getTypeAtLocation(callee), + ) + ) { + return; + } + + const executor = node.arguments.at(0); + if (!executor || !isFunction(executor)) { + return; + } + const rejectParamNode = executor.params.at(1); + if (!rejectParamNode || !isIdentifier(rejectParamNode)) { + return; + } + + // reject param is always present in variables declared by executor + const rejectVariable = getDeclaredVariables(context, executor).find( + variable => variable.identifiers.includes(rejectParamNode), + )!; + + rejectVariable.references.forEach(ref => { + if ( + ref.identifier.parent.type !== AST_NODE_TYPES.CallExpression || + ref.identifier !== ref.identifier.parent.callee + ) { + return; + } + + checkRejectCall(ref.identifier.parent); + }); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-array-delete.test.ts b/packages/eslint-plugin/tests/rules/no-array-delete.test.ts new file mode 100644 index 000000000000..ac803cb5b2f8 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-array-delete.test.ts @@ -0,0 +1,599 @@ +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; + +import rule from '../../src/rules/no-array-delete'; +import { getFixturesRootDir } from '../RuleTester'; + +const rootPath = getFixturesRootDir(); + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: rootPath, + project: './tsconfig.json', + }, +}); + +ruleTester.run('no-array-delete', rule, { + valid: [ + ` + declare const obj: { a: 1; b: 2 }; + delete obj.a; + `, + + ` + declare const obj: { a: 1; b: 2 }; + delete obj['a']; + `, + + ` + declare const arr: { a: 1; b: 2 }[][][][]; + delete arr[0][0][0][0].a; + `, + + ` + declare const maybeArray: any; + delete maybeArray[0]; + `, + + ` + declare const maybeArray: unknown; + delete maybeArray[0]; + `, + + ` + declare function getObject(): T; + delete getObject().a; + `, + + ` + declare function getObject(): { a: T; b: 2 }; + delete getObject().a; + `, + + ` + declare const test: never; + delete test[0]; + `, + ], + + invalid: [ + { + code: ` + declare const arr: number[]; + delete arr[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: number[]; + arr.splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: number[]; + declare const key: number; + delete arr[key]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 4, + column: 9, + endColumn: 24, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: number[]; + declare const key: number; + arr.splice(key, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: number[]; + + enum Keys { + A, + B, + } + + delete arr[Keys.A]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 9, + column: 9, + endColumn: 27, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: number[]; + + enum Keys { + A, + B, + } + + arr.splice(Keys.A, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: number[]; + declare function doWork(): void; + delete arr[(doWork(), 1)]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 4, + column: 9, + endColumn: 34, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: number[]; + declare function doWork(): void; + arr.splice((doWork(), 1), 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: Array; + delete arr[0]; + `, + + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: Array; + arr.splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: 'delete [1, 2, 3][0];', + errors: [ + { + messageId: 'noArrayDelete', + line: 1, + column: 1, + endColumn: 20, + suggestions: [ + { + messageId: 'useSplice', + output: '[1, 2, 3].splice(0, 1);', + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: unknown[]; + delete arr[Math.random() ? 0 : 1]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 42, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: unknown[]; + arr.splice(Math.random() ? 0 : 1, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: number[] | string[] | boolean[]; + delete arr[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: number[] | string[] | boolean[]; + arr.splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: number[] & unknown; + delete arr[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: number[] & unknown; + arr.splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const arr: (number | string)[]; + delete arr[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const arr: (number | string)[]; + arr.splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const obj: { a: { b: { c: number[] } } }; + delete obj.a.b.c[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 28, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const obj: { a: { b: { c: number[] } } }; + obj.a.b.c.splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare function getArray(): T; + delete getArray()[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 29, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare function getArray(): T; + getArray().splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare function getArray(): T[]; + delete getArray()[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 29, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare function getArray(): T[]; + getArray().splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + function deleteFromArray(a: number[]) { + delete a[0]; + } + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 11, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + function deleteFromArray(a: number[]) { + a.splice(0, 1); + } + `, + }, + ], + }, + ], + }, + + { + code: ` + function deleteFromArray(a: T[]) { + delete a[0]; + } + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 11, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + function deleteFromArray(a: T[]) { + a.splice(0, 1); + } + `, + }, + ], + }, + ], + }, + + { + code: ` + function deleteFromArray(a: T) { + delete a[0]; + } + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 11, + endColumn: 22, + suggestions: [ + { + messageId: 'useSplice', + output: ` + function deleteFromArray(a: T) { + a.splice(0, 1); + } + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const tuple: [number, string]; + delete tuple[0]; + `, + errors: [ + { + messageId: 'noArrayDelete', + line: 3, + column: 9, + endColumn: 24, + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const tuple: [number, string]; + tuple.splice(0, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const a: number[]; + declare const b: number; + + delete [...a, ...a][b]; + `, + errors: [ + { + messageId: 'noArrayDelete', + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const a: number[]; + declare const b: number; + + [...a, ...a].splice(b, 1); + `, + }, + ], + }, + ], + }, + + { + code: noFormat` + declare const a: number[]; + declare const b: number; + + // before expression + delete /** multi + line */ a[(( + // single-line + b /* inline */ /* another-inline */ ) + ) /* another-one */ ] /* before semicolon */; /* after semicolon */ + // after expression + `, + errors: [ + { + messageId: 'noArrayDelete', + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const a: number[]; + declare const b: number; + + // before expression + /** multi + line */ + // single-line + /* inline */ + /* another-inline */ + /* another-one */ + a.splice(b, 1) /* before semicolon */; /* after semicolon */ + // after expression + `, + }, + ], + }, + ], + }, + + { + code: noFormat` + declare const a: number[]; + declare const b: number; + + delete ((a[((b))])); + `, + errors: [ + { + messageId: 'noArrayDelete', + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const a: number[]; + declare const b: number; + + a.splice(b, 1); + `, + }, + ], + }, + ], + }, + + { + code: ` + declare const a: number[]; + declare const b: number; + + delete a[(b + 1) * (b + 2)]; + `, + errors: [ + { + messageId: 'noArrayDelete', + suggestions: [ + { + messageId: 'useSplice', + output: ` + declare const a: number[]; + declare const b: number; + + a.splice((b + 1) * (b + 2), 1); + `, + }, + ], + }, + ], + }, + ], +}); 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 8cf362938bab..2c3cc3599e83 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 @@ -48,6 +48,11 @@ const foo = { 0: 'hello', 5: 'hello' } as PossibleTuple; ` let bar: number | undefined = x; let foo: number = bar!; + `, + ` +declare const a: { data?: unknown }; + +const x = a.data!; `, { code: ` @@ -213,6 +218,30 @@ let values: number[] = []; value = values.pop()!; `, + ` +declare function foo(): number | undefined; +const a = foo()!; + `, + ` +declare function foo(): number | undefined; +const a = foo() as number; + `, + ` +declare function foo(): number | undefined; +const a = foo(); + `, + ` +declare const arr: (object | undefined)[]; +const item = arr[0]!; + `, + ` +declare const arr: (object | undefined)[]; +const item = arr[0] as object; + `, + ` +declare const arr: (object | undefined)[]; +const item = arr[0]; + `, ], invalid: [ @@ -518,5 +547,118 @@ y = 0; }, ], }, + { + code: ` +declare function foo(): number; +const a = foo()!; + `, + output: ` +declare function foo(): number; +const a = foo(); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 3, + column: 11, + endColumn: 17, + }, + ], + }, + { + code: ` +const b = new Date()!; + `, + output: ` +const b = new Date(); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + }, + ], + }, + { + code: ` +const b = (1 + 1)!; + `, + output: ` +const b = (1 + 1); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 2, + column: 11, + endColumn: 19, + }, + ], + }, + { + code: ` +declare function foo(): number; +const a = foo() as number; + `, + output: ` +declare function foo(): number; +const a = foo(); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 3, + column: 11, + }, + ], + }, + { + code: ` +declare function foo(): number; +const a = foo(); + `, + output: ` +declare function foo(): number; +const a = foo(); + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + line: 3, + }, + ], + }, + { + code: ` +type RT = { log: () => void }; +declare function foo(): RT; +(foo() as RT).log; + `, + output: ` +type RT = { log: () => void }; +declare function foo(): RT; +(foo()).log; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + }, + ], + }, + { + code: ` +declare const arr: object[]; +const item = arr[0]!; + `, + output: ` +declare const arr: object[]; +const item = arr[0]; + `, + errors: [ + { + messageId: 'unnecessaryAssertion', + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts index 54ac89c2c797..674b4ccefe77 100644 --- a/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts +++ b/packages/eslint-plugin/tests/rules/no-useless-template-literals.test.ts @@ -137,6 +137,7 @@ ruleTester.run('no-useless-template-literals', rule, { invalid: [ { code: '`${1}`;', + output: '`1`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -146,19 +147,50 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + { - code: '`${1n}`;', + code: noFormat`\`\${ 1 }\`;`, + output: '`1`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: noFormat`\`\${ 'a' }\`;`, + output: `'a';`, errors: [ { messageId: 'noUselessTemplateLiteral', - line: 1, - column: 4, - endColumn: 6, }, ], }, + + { + code: noFormat`\`\${ "a" }\`;`, + output: `"a";`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: noFormat`\`\${ 'a' + 'b' }\`;`, + output: `'a' + 'b';`, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: '`${true}`;', + output: '`true`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -168,8 +200,20 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: noFormat`\`\${ true }\`;`, + output: '`true`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: '`${null}`;', + output: '`null`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -179,8 +223,20 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: noFormat`\`\${ null }\`;`, + output: '`null`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: '`${undefined}`;', + output: '`undefined`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -190,8 +246,20 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + { - code: "`${'a'}${'b'}`;", + code: noFormat`\`\${ undefined }\`;`, + output: '`undefined`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`${'a'} ${'b'}`;", + output: '`a b`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -202,8 +270,21 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 1, - column: 10, - endColumn: 13, + column: 11, + endColumn: 14, + }, + ], + }, + + { + code: noFormat`\`\${ 'a' } \${ 'b' }\`;`, + output: '`a b`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + { + messageId: 'noUselessTemplateLiteral', }, ], }, @@ -213,6 +294,10 @@ ruleTester.run('no-useless-template-literals', rule, { declare const b: 'b'; \`a\${b}\${'c'}\`; `, + output: ` + declare const b: 'b'; + \`a\${b}c\`; + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -225,6 +310,7 @@ ruleTester.run('no-useless-template-literals', rule, { { code: "`a${'b'}`;", + output: '`ab`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -236,13 +322,14 @@ ruleTester.run('no-useless-template-literals', rule, { }, { - code: "`${'1 + 1 = '}${2}`;", + code: "`${'1 + 1 ='} ${2}`;", + output: '`1 + 1 = 2`;', errors: [ { messageId: 'noUselessTemplateLiteral', line: 1, column: 4, - endColumn: 14, + endColumn: 13, }, { messageId: 'noUselessTemplateLiteral', @@ -254,7 +341,8 @@ ruleTester.run('no-useless-template-literals', rule, { }, { - code: "`${'a'}${true}`;", + code: "`${'a'} ${true}`;", + output: '`a true`;', errors: [ { messageId: 'noUselessTemplateLiteral', @@ -265,8 +353,8 @@ ruleTester.run('no-useless-template-literals', rule, { { messageId: 'noUselessTemplateLiteral', line: 1, - column: 10, - endColumn: 14, + column: 11, + endColumn: 15, }, ], }, @@ -276,6 +364,10 @@ ruleTester.run('no-useless-template-literals', rule, { declare const string: 'a'; \`\${string}\`; `, + output: ` + declare const string: 'a'; + string; + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -286,8 +378,25 @@ ruleTester.run('no-useless-template-literals', rule, { ], }, + { + code: noFormat` + declare const string: 'a'; + \`\${ string }\`; + `, + output: ` + declare const string: 'a'; + string; + `, + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + { code: "`${String(Symbol.for('test'))}`;", + output: "String(Symbol.for('test'));", errors: [ { messageId: 'noUselessTemplateLiteral', @@ -303,6 +412,10 @@ ruleTester.run('no-useless-template-literals', rule, { declare const intersection: string & { _brand: 'test-brand' }; \`\${intersection}\`; `, + output: ` + declare const intersection: string & { _brand: 'test-brand' }; + intersection; + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -319,6 +432,11 @@ ruleTester.run('no-useless-template-literals', rule, { \`\${arg}\`; } `, + output: ` + function func(arg: T) { + arg; + } + `, errors: [ { messageId: 'noUselessTemplateLiteral', @@ -328,5 +446,65 @@ ruleTester.run('no-useless-template-literals', rule, { }, ], }, + + { + code: "`${'`'}`;", + output: "'`';", + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`back${'`'}tick`;", + output: '`back\\`tick`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`dollar${'${`this is test`}'}sign`;", + output: '`dollar\\${\\`this is test\\`}sign`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: '`complex${\'`${"`${test}`"}`\'}case`;', + output: '`complex\\`\\${"\\`\\${test}\\`"}\\`case`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`some ${'\\\\${test}'} string`;", + output: '`some \\\\\\${test} string`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, + + { + code: "`some ${'\\\\`'} string`;", + output: '`some \\\\\\` string`;', + errors: [ + { + messageId: 'noUselessTemplateLiteral', + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/prefer-promise-reject-errors.test.ts b/packages/eslint-plugin/tests/rules/prefer-promise-reject-errors.test.ts new file mode 100644 index 000000000000..69eed002ebf7 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/prefer-promise-reject-errors.test.ts @@ -0,0 +1,1438 @@ +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + +import rule from '../../src/rules/prefer-promise-reject-errors'; +import { getFixturesRootDir } from '../RuleTester'; + +const rootDir = getFixturesRootDir(); +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2015, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('prefer-promise-reject-errors', rule, { + valid: [ + 'Promise.resolve(5);', + { + code: 'Promise.reject();', + options: [ + { + allowEmptyReject: true, + }, + ], + }, + 'Promise.reject(new Error());', + 'Promise.reject(new TypeError());', + "Promise.reject(new Error('foo'));", + ` + class CustomError extends Error {} + Promise.reject(new CustomError()); + `, + ` + declare const foo: () => { err: SyntaxError }; + Promise.reject(foo().err); + `, + ` + declare const foo: () => Promise; + Promise.reject(await foo()); + `, + 'Promise.reject((foo = new Error()));', + ` + const foo = Promise; + foo.reject(new Error()); + `, + "Promise['reject'](new Error());", + 'Promise.reject(true && new Error());', + ` + const foo = false; + Promise.reject(false || new Error()); + `, + ` + declare const foo: Readonly; + Promise.reject(foo); + `, + ` + declare const foo: Readonly | Readonly; + Promise.reject(foo); + `, + ` + declare const foo: Readonly & Readonly; + Promise.reject(foo); + `, + ` + declare const foo: Readonly & { foo: 'bar' }; + Promise.reject(foo); + `, + ` + declare const foo: Readonly & { foo: 'bar' }; + Promise.reject(foo); + `, + ` + declare const foo: Readonly>; + Promise.reject(foo); + `, + ` + declare const foo: Readonly>>; + Promise.reject(foo); + `, + ` + declare const foo: Readonly< + Readonly & { foo: 'bar' }> & { + fooBar: 'barFoo'; + } + > & { barFoo: 'fooBar' }; + Promise.reject(foo); + `, + ` + declare const foo: + | Readonly | Readonly> + | Readonly; + Promise.reject(foo); + `, + ` + type Wrapper = { foo: Readonly[] }; + declare const foo: Wrapper['foo'][5]; + Promise.reject(foo); + `, + ` + declare const foo: Error[]; + Promise.reject(foo[5]); + `, + ` + declare const foo: ReadonlyArray; + Promise.reject(foo[5]); + `, + ` + declare const foo: [Error]; + Promise.reject(foo[0]); + `, + + ` + new Promise(function (resolve, reject) { + resolve(5); + }); + `, + ` + new Promise(function (resolve, reject) { + reject(new Error()); + }); + `, + ` + new Promise((resolve, reject) => { + reject(new Error()); + }); + `, + 'new Promise((resolve, reject) => reject(new Error()));', + { + code: ` + new Promise(function (resolve, reject) { + reject(); + }); + `, + options: [ + { + allowEmptyReject: true, + }, + ], + }, + 'new Promise((yes, no) => no(new Error()));', + 'new Promise();', + 'new Promise(5);', + 'new Promise((resolve, { apply }) => {});', + 'new Promise((resolve, reject) => {});', + 'new Promise((resolve, reject) => reject);', + ` + class CustomError extends Error {} + new Promise(function (resolve, reject) { + reject(new CustomError()); + }); + `, + ` + declare const foo: () => { err: SyntaxError }; + new Promise(function (resolve, reject) { + reject(foo().err); + }); + `, + 'new Promise((resolve, reject) => reject((foo = new Error())));', + ` + new Foo((resolve, reject) => reject(5)); + `, + ` + class Foo { + constructor( + executor: (resolve: () => void, reject: (reason?: any) => void) => void, + ): Promise {} + } + new Foo((resolve, reject) => reject(5)); + `, + ` + new Promise((resolve, reject) => { + return function (reject) { + reject(5); + }; + }); + `, + 'new Promise((resolve, reject) => resolve(5, reject));', + ` + class C { + #error: Error; + foo() { + Promise.reject(this.#error); + } + } + `, + ` + const foo = Promise; + new foo((resolve, reject) => reject(new Error())); + `, + ` + declare const foo: Readonly; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Readonly | Readonly; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Readonly & Readonly; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Readonly & { foo: 'bar' }; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Readonly & { foo: 'bar' }; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Readonly>; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Readonly>>; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Readonly< + Readonly & { foo: 'bar' }> & { + fooBar: 'barFoo'; + } + > & { barFoo: 'fooBar' }; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: + | Readonly | Readonly> + | Readonly; + new Promise((resolve, reject) => reject(foo)); + `, + ` + type Wrapper = { foo: Readonly[] }; + declare const foo: Wrapper['foo'][5]; + new Promise((resolve, reject) => reject(foo)); + `, + ` + declare const foo: Error[]; + new Promise((resolve, reject) => reject(foo[5])); + `, + ` + declare const foo: ReadonlyArray; + new Promise((resolve, reject) => reject(foo[5])); + `, + ` + declare const foo: [Error]; + new Promise((resolve, reject) => reject(foo[0])); + `, + ` + class Foo extends Promise {} + Foo.reject(new Error()); + `, + ` + class Foo extends Promise {} + new Foo((resolve, reject) => reject(new Error())); + `, + ` + declare const someRandomCall: { + reject(arg: any): void; + }; + someRandomCall.reject(5); + `, + ` + declare const foo: PromiseConstructor; + foo.reject(new Error()); + `, + ], + invalid: [ + { + code: 'Promise.reject(5);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 18, + }, + ], + }, + { + code: "Promise.reject('foo');", + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 22, + }, + ], + }, + { + code: 'Promise.reject(`foo`);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 22, + }, + ], + }, + { + code: "Promise.reject('foo', somethingElse);", + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 37, + }, + ], + }, + { + code: 'Promise.reject(false);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 22, + }, + ], + }, + { + code: 'Promise.reject(void `foo`);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 27, + }, + ], + }, + { + code: 'Promise.reject();', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 17, + }, + ], + }, + { + code: 'Promise.reject(undefined);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 26, + }, + ], + }, + { + code: 'Promise.reject(undefined);', + options: [{ allowEmptyReject: true }], + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 26, + }, + ], + }, + { + code: 'Promise.reject(null);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 21, + }, + ], + }, + { + code: 'Promise.reject({ foo: 1 });', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 27, + }, + ], + }, + { + code: 'Promise.reject([1, 2, 3]);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 26, + }, + ], + }, + { + code: ` +declare const foo: Error | undefined; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: () => Promise; +Promise.reject(await foo()); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 28, + }, + ], + }, + { + code: ` +declare const foo: boolean; +Promise.reject(foo && new Error()); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 35, + }, + ], + }, + { + code: ` +const foo = Promise; +foo.reject(); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 13, + }, + ], + }, + + { + code: 'Promise.reject?.(5);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: 'Promise?.reject(5);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 19, + }, + ], + }, + { + code: 'Promise?.reject?.(5);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 21, + }, + ], + }, + { + code: '(Promise?.reject)(5);', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 21, + }, + ], + }, + { + code: noFormat`(Promise?.reject)?.(5);`, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 23, + }, + ], + }, + { + code: "Promise['reject'](5);", + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 21, + }, + ], + }, + + // Assignments with mathematical operators will either evaluate to a primitive value or throw a TypeError + { + code: 'Promise.reject((foo += new Error()));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 37, + }, + ], + }, + { + code: 'Promise.reject((foo -= new Error()));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 37, + }, + ], + }, + { + code: 'Promise.reject((foo **= new Error()));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 38, + }, + ], + }, + { + code: 'Promise.reject((foo <<= new Error()));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 38, + }, + ], + }, + { + code: 'Promise.reject((foo |= new Error()));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 37, + }, + ], + }, + { + code: 'Promise.reject((foo &= new Error()));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 1, + endColumn: 37, + }, + ], + }, + { + code: ` +declare const foo: never; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: unknown; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +type FakeReadonly = { 'fake readonly': T }; +declare const foo: FakeReadonly; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 4, + endLine: 4, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly<'error'>; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly | 'error'; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly | Readonly | Readonly<'error'>; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly>; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly | 'error'>>; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly & TypeError>> | 'error'; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Readonly> | Readonly | 'error'; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +type Wrapper = { foo: Readonly[] }; +declare const foo: Wrapper['foo'][5]; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 4, + endLine: 4, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: Error[]; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: ReadonlyArray; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + { + code: ` +declare const foo: [Error]; +Promise.reject(foo); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 20, + }, + ], + }, + + { + code: ` +new Promise(function (resolve, reject) { + reject(); +}); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 3, + endColumn: 11, + }, + ], + }, + { + code: ` +new Promise(function (resolve, reject) { + reject(5); +}); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 3, + endColumn: 12, + }, + ], + }, + { + code: ` +new Promise((resolve, reject) => { + reject(); +}); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 3, + endColumn: 11, + }, + ], + }, + { + code: 'new Promise((resolve, reject) => reject(5));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 34, + endColumn: 43, + }, + ], + }, + { + code: ` +new Promise((resolve, reject) => { + fs.readFile('foo.txt', (err, file) => { + if (err) reject('File not found'); + else resolve(file); + }); +}); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 4, + endLine: 4, + column: 14, + endColumn: 38, + }, + ], + }, + { + code: 'new Promise((yes, no) => no(5));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 26, + endColumn: 31, + }, + ], + }, + { + code: 'new Promise(({ foo, bar, baz }, reject) => reject(5));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 44, + endColumn: 53, + }, + ], + }, + { + code: ` +new Promise(function (reject, reject) { + reject(5); +}); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 3, + endColumn: 12, + }, + ], + }, + { + code: ` +new Promise(function (foo, arguments) { + arguments(5); +}); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 3, + endColumn: 15, + }, + ], + }, + { + code: 'new Promise((foo, arguments) => arguments(5));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 33, + endColumn: 45, + }, + ], + }, + { + code: ` +new Promise(function ({}, reject) { + reject(5); +}); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 3, + endColumn: 12, + }, + ], + }, + { + code: 'new Promise(({}, reject) => reject(5));', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 29, + endColumn: 38, + }, + ], + }, + { + code: 'new Promise((resolve, reject, somethingElse = reject(5)) => {});', + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 1, + endLine: 1, + column: 47, + endColumn: 56, + }, + ], + }, + { + code: ` +declare const foo: { + bar: PromiseConstructor; +}; +new foo.bar((resolve, reject) => reject(5)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 5, + endLine: 5, + column: 34, + endColumn: 43, + }, + ], + }, + { + code: ` +declare const foo: { + bar: PromiseConstructor; +}; +new (foo?.bar)((resolve, reject) => reject(5)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 5, + endLine: 5, + column: 37, + endColumn: 46, + }, + ], + }, + { + code: ` +const foo = Promise; +new foo((resolve, reject) => reject(5)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 30, + endColumn: 39, + }, + ], + }, + { + code: ` +declare const foo: never; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: unknown; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +type FakeReadonly = { 'fake readonly': T }; +declare const foo: FakeReadonly; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 4, + endLine: 4, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly<'error'>; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly | 'error'; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly | Readonly | Readonly<'error'>; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly>; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly | 'error'>>; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly & TypeError>> | 'error'; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Readonly> | Readonly | 'error'; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +type Wrapper = { foo: Readonly[] }; +declare const foo: Wrapper['foo'][5]; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 4, + endLine: 4, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: Error[]; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: ReadonlyArray; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +declare const foo: [Error]; +new Promise((resolve, reject) => reject(foo)); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 34, + endColumn: 45, + }, + ], + }, + { + code: ` +class Foo extends Promise {} +Foo.reject(5); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 14, + }, + ], + }, + { + code: ` +declare const foo: PromiseConstructor & string; +foo.reject(5); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 3, + endLine: 3, + column: 1, + endColumn: 14, + }, + ], + }, + { + code: ` +class Foo extends Promise {} +class Bar extends Foo {} +Bar.reject(5); + `, + errors: [ + { + messageId: 'rejectAnError', + type: AST_NODE_TYPES.CallExpression, + line: 4, + endLine: 4, + column: 1, + endColumn: 14, + }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin/tests/schema-snapshots/no-array-delete.shot b/packages/eslint-plugin/tests/schema-snapshots/no-array-delete.shot new file mode 100644 index 000000000000..c8dc106464a0 --- /dev/null +++ b/packages/eslint-plugin/tests/schema-snapshots/no-array-delete.shot @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Rule schemas should be convertible to TS types for documentation purposes no-array-delete 1`] = ` +" +# SCHEMA: + +[] + + +# TYPES: + +/** No options declared */ +type Options = [];" +`; diff --git a/packages/eslint-plugin/tests/schema-snapshots/prefer-promise-reject-errors.shot b/packages/eslint-plugin/tests/schema-snapshots/prefer-promise-reject-errors.shot new file mode 100644 index 000000000000..fc04d11fd3f1 --- /dev/null +++ b/packages/eslint-plugin/tests/schema-snapshots/prefer-promise-reject-errors.shot @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Rule schemas should be convertible to TS types for documentation purposes prefer-promise-reject-errors 1`] = ` +" +# SCHEMA: + +[ + { + "additionalProperties": false, + "properties": { + "allowEmptyReject": { + "type": "boolean" + } + }, + "type": "object" + } +] + + +# TYPES: + +type Options = [ + { + allowEmptyReject?: boolean; + }, +]; +" +`; diff --git a/packages/integration-tests/CHANGELOG.md b/packages/integration-tests/CHANGELOG.md index 01956c239599..d254a11713de 100644 --- a/packages/integration-tests/CHANGELOG.md +++ b/packages/integration-tests/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for integration-tests to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for integration-tests to align it with other projects, there were no code changes. diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index d917a1b7d0c5..cc57c27bd0c6 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/integration-tests", - "version": "6.18.1", + "version": "6.19.0", "private": true, "scripts": { "format": "prettier --write \"./**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx,json,md,css}\" --ignore-path ../../.prettierignore", diff --git a/packages/integration-tests/tools/integration-test-base.ts b/packages/integration-tests/tools/integration-test-base.ts index eb35212ee775..833ec45182a0 100644 --- a/packages/integration-tests/tools/integration-test-base.ts +++ b/packages/integration-tests/tools/integration-test-base.ts @@ -94,6 +94,8 @@ export function integrationTest(testFilename: string, filesGlob: string): void { if (stderr.length > 0) { console.error(stderr); } + // childProcess.ExecFileException is an extension of Error + // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors reject(err); } else { resolve(); diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 49608f459c04..e5fbedc0de30 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for parser to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for parser to align it with other projects, there were no code changes. diff --git a/packages/parser/package.json b/packages/parser/package.json index 48005cd3604d..3f314a9c0a3f 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "6.18.1", + "version": "6.19.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "files": [ "dist", @@ -51,10 +51,10 @@ "eslint": "^7.0.0 || ^8.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4" }, "devDependencies": { diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index d8f38af87fd4..a3a213849d57 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for repo-tools to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for repo-tools to align it with other projects, there were no code changes. diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 593a072fbb2c..527094065401 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/repo-tools", - "version": "6.18.1", + "version": "6.19.0", "private": true, "scripts": { "//": "NOTE: intentionally no build step in this package", diff --git a/packages/rule-schema-to-typescript-types/CHANGELOG.md b/packages/rule-schema-to-typescript-types/CHANGELOG.md index 7c442df6eefd..663193236149 100644 --- a/packages/rule-schema-to-typescript-types/CHANGELOG.md +++ b/packages/rule-schema-to-typescript-types/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for rule-schema-to-typescript-types to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for rule-schema-to-typescript-types to align it with other projects, there were no code changes. diff --git a/packages/rule-schema-to-typescript-types/package.json b/packages/rule-schema-to-typescript-types/package.json index bf42b1752a85..2004f67e90de 100644 --- a/packages/rule-schema-to-typescript-types/package.json +++ b/packages/rule-schema-to-typescript-types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/rule-schema-to-typescript-types", - "version": "6.18.1", + "version": "6.19.0", "private": true, "type": "commonjs", "exports": { @@ -34,8 +34,8 @@ }, "dependencies": { "@prettier/sync": "*", - "@typescript-eslint/type-utils": "6.18.1", - "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "natural-compare": "^1.4.0", "prettier": "^3.0.3" }, diff --git a/packages/rule-tester/CHANGELOG.md b/packages/rule-tester/CHANGELOG.md index 83ba6ce3089e..2c5785181b72 100644 --- a/packages/rule-tester/CHANGELOG.md +++ b/packages/rule-tester/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for rule-tester to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for rule-tester to align it with other projects, there were no code changes. diff --git a/packages/rule-tester/package.json b/packages/rule-tester/package.json index f9f2ba2ffdb3..fc613092dbd2 100644 --- a/packages/rule-tester/package.json +++ b/packages/rule-tester/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/rule-tester", - "version": "6.18.1", + "version": "6.19.0", "description": "Tooling to test ESLint rules", "files": [ "dist", @@ -47,8 +47,8 @@ }, "//": "NOTE - AJV is out-of-date, but it's intentionally synced with ESLint - https://github.com/eslint/eslint/blob/ad9dd6a933fd098a0d99c6a9aa059850535c23ee/package.json#L70", "dependencies": { - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "ajv": "^6.10.0", "lodash.merge": "4.6.2", "semver": "^7.5.4" @@ -59,7 +59,7 @@ }, "devDependencies": { "@types/lodash.merge": "4.6.9", - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/parser": "6.19.0", "chai": "^4.3.7", "mocha": "^10.0.0", "sinon": "^16.0.0", diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md index 894664c687c0..4cf6f4348675 100644 --- a/packages/scope-manager/CHANGELOG.md +++ b/packages/scope-manager/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for scope-manager to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for scope-manager to align it with other projects, there were no code changes. diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json index edd3869831f6..d80e065def42 100644 --- a/packages/scope-manager/package.json +++ b/packages/scope-manager/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/scope-manager", - "version": "6.18.1", + "version": "6.19.0", "description": "TypeScript scope analyser for ESLint", "files": [ "dist", @@ -44,13 +44,13 @@ "typecheck": "npx nx typecheck" }, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" }, "devDependencies": { "@prettier/sync": "*", "@types/glob": "*", - "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/typescript-estree": "6.19.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/type-utils/CHANGELOG.md b/packages/type-utils/CHANGELOG.md index 0f5e135859a1..f90bb7b61fc6 100644 --- a/packages/type-utils/CHANGELOG.md +++ b/packages/type-utils/CHANGELOG.md @@ -1,3 +1,28 @@ +## 6.19.0 (2024-01-15) + + +### 🚀 Features + +- **eslint-plugin:** [prefer-promise-reject-errors] add rule + + +### 🩹 Fixes + +- **eslint-plugin:** [no-unnecesary-type-assertion] treat unknown/any as nullable + + +### ❤️ Thank You + +- auvred +- Brad Zacher +- Josh Goldberg ✨ +- Joshua Chen +- LJX +- Steven +- StyleShit + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for type-utils to align it with other projects, there were no code changes. diff --git a/packages/type-utils/package.json b/packages/type-utils/package.json index 6548eeb9c8a7..d3635aabf5d6 100644 --- a/packages/type-utils/package.json +++ b/packages/type-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/type-utils", - "version": "6.18.1", + "version": "6.19.0", "description": "Type utilities for working with TypeScript + ESLint together", "files": [ "dist", @@ -45,13 +45,13 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "devDependencies": { - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/parser": "6.19.0", "ajv": "^6.10.0", "downlevel-dts": "*", "jest": "29.7.0", diff --git a/packages/type-utils/src/builtinSymbolLikes.ts b/packages/type-utils/src/builtinSymbolLikes.ts new file mode 100644 index 000000000000..3443a0d0382e --- /dev/null +++ b/packages/type-utils/src/builtinSymbolLikes.ts @@ -0,0 +1,161 @@ +import * as ts from 'typescript'; + +import { isSymbolFromDefaultLibrary } from './isSymbolFromDefaultLibrary'; + +/** + * class Foo extends Promise {} + * Foo.reject + * ^ PromiseLike + */ +export function isPromiseLike(program: ts.Program, type: ts.Type): boolean { + return isBuiltinSymbolLike(program, type, 'Promise'); +} + +/** + * const foo = Promise + * foo.reject + * ^ PromiseConstructorLike + */ +export function isPromiseConstructorLike( + program: ts.Program, + type: ts.Type, +): boolean { + return isBuiltinSymbolLike(program, type, 'PromiseConstructor'); +} + +/** + * class Foo extends Error {} + * new Foo() + * ^ ErrorLike + */ +export function isErrorLike(program: ts.Program, type: ts.Type): boolean { + return isBuiltinSymbolLike(program, type, 'Error'); +} + +/** + * type T = Readonly + * ^ ReadonlyErrorLike + */ +export function isReadonlyErrorLike( + program: ts.Program, + type: ts.Type, +): boolean { + return isReadonlyTypeLike(program, type, subtype => { + const [typeArgument] = subtype.aliasTypeArguments; + return ( + isErrorLike(program, typeArgument) || + isReadonlyErrorLike(program, typeArgument) + ); + }); +} + +/** + * type T = Readonly<{ foo: 'bar' }> + * ^ ReadonlyTypeLike + */ +export function isReadonlyTypeLike( + program: ts.Program, + type: ts.Type, + predicate?: ( + subType: ts.Type & { + aliasSymbol: ts.Symbol; + aliasTypeArguments: readonly ts.Type[]; + }, + ) => boolean, +): boolean { + return isBuiltinTypeAliasLike(program, type, subtype => { + return ( + subtype.aliasSymbol.getName() === 'Readonly' && !!predicate?.(subtype) + ); + }); +} +export function isBuiltinTypeAliasLike( + program: ts.Program, + type: ts.Type, + predicate: ( + subType: ts.Type & { + aliasSymbol: ts.Symbol; + aliasTypeArguments: readonly ts.Type[]; + }, + ) => boolean, +): boolean { + return isBuiltinSymbolLikeRecurser(program, type, subtype => { + const { aliasSymbol, aliasTypeArguments } = subtype; + + if (!aliasSymbol || !aliasTypeArguments) { + return false; + } + + if ( + isSymbolFromDefaultLibrary(program, aliasSymbol) && + predicate( + subtype as ts.Type & { + aliasSymbol: ts.Symbol; + aliasTypeArguments: readonly ts.Type[]; + }, + ) + ) { + return true; + } + + return null; + }); +} + +export function isBuiltinSymbolLike( + program: ts.Program, + type: ts.Type, + symbolName: string, +): boolean { + return isBuiltinSymbolLikeRecurser(program, type, subType => { + const symbol = subType.getSymbol(); + if (!symbol) { + return false; + } + + if ( + symbol.getName() === symbolName && + isSymbolFromDefaultLibrary(program, symbol) + ) { + return true; + } + + return null; + }); +} + +export function isBuiltinSymbolLikeRecurser( + program: ts.Program, + type: ts.Type, + predicate: (subType: ts.Type) => boolean | null, +): boolean { + if (type.isIntersection()) { + return type.types.some(t => + isBuiltinSymbolLikeRecurser(program, t, predicate), + ); + } + if (type.isUnion()) { + return type.types.every(t => + isBuiltinSymbolLikeRecurser(program, t, predicate), + ); + } + + const predicateResult = predicate(type); + if (typeof predicateResult === 'boolean') { + return predicateResult; + } + + const symbol = type.getSymbol(); + if ( + symbol && + symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface) + ) { + const checker = program.getTypeChecker(); + for (const baseType of checker.getBaseTypes(type as ts.InterfaceType)) { + if (isBuiltinSymbolLikeRecurser(program, baseType, predicate)) { + return true; + } + } + } + return false; +} diff --git a/packages/type-utils/src/index.ts b/packages/type-utils/src/index.ts index 9fc499aa8f31..14d5b652099f 100644 --- a/packages/type-utils/src/index.ts +++ b/packages/type-utils/src/index.ts @@ -1,3 +1,4 @@ +export * from './builtinSymbolLikes'; export * from './containsAllTypesByName'; export * from './getConstrainedTypeAtLocation'; export * from './getContextualType'; @@ -6,6 +7,7 @@ export * from './getSourceFileOfNode'; export * from './getTokenAtPosition'; export * from './getTypeArguments'; export * from './getTypeName'; +export * from './isSymbolFromDefaultLibrary'; export * from './isTypeReadonly'; export * from './isUnsafeAssignment'; export * from './predicates'; diff --git a/packages/type-utils/src/isSymbolFromDefaultLibrary.ts b/packages/type-utils/src/isSymbolFromDefaultLibrary.ts new file mode 100644 index 000000000000..786ef849a2c4 --- /dev/null +++ b/packages/type-utils/src/isSymbolFromDefaultLibrary.ts @@ -0,0 +1,20 @@ +import type * as ts from 'typescript'; + +export function isSymbolFromDefaultLibrary( + program: ts.Program, + symbol: ts.Symbol | undefined, +): boolean { + if (!symbol) { + return false; + } + + const declarations = symbol.getDeclarations() ?? []; + for (const declaration of declarations) { + const sourceFile = declaration.getSourceFile(); + if (program.isSourceFileDefaultLibrary(sourceFile)) { + return true; + } + } + + return false; +} diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts index 37658e31b716..26ef59355781 100644 --- a/packages/type-utils/src/predicates.ts +++ b/packages/type-utils/src/predicates.ts @@ -2,15 +2,18 @@ import debug from 'debug'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; -import { getTypeFlags, isTypeFlagSet } from './typeFlagUtils'; +import { isTypeFlagSet } from './typeFlagUtils'; const log = debug('typescript-eslint:eslint-plugin:utils:types'); export interface IsNullableTypeOptions { /** - * Whether the type is a receiving type (i.e. the type of a called function's parameter). + * @deprecated - this flag no longer does anything and will be removed in the next major */ isReceiver?: boolean; + /** + * @deprecated - this flag no longer does anything and will be removed in the next major + */ allowUndefined?: boolean; } @@ -19,18 +22,15 @@ export interface IsNullableTypeOptions { */ export function isNullableType( type: ts.Type, - { isReceiver = false, allowUndefined = true }: IsNullableTypeOptions = {}, + _deprecated?: IsNullableTypeOptions, ): boolean { - const flags = getTypeFlags(type); - - if (isReceiver && flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { - return true; - } - - if (allowUndefined) { - return (flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) !== 0; - } - return (flags & ts.TypeFlags.Null) !== 0; + return isTypeFlagSet( + type, + ts.TypeFlags.Any | + ts.TypeFlags.Unknown | + ts.TypeFlags.Null | + ts.TypeFlags.Undefined, + ); } /** diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 37f72e1df92d..ee88f19fed34 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for types to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for types to align it with other projects, there were no code changes. diff --git a/packages/types/package.json b/packages/types/package.json index 063eb0e13657..e6484f0156ef 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/types", - "version": "6.18.1", + "version": "6.19.0", "description": "Types for the TypeScript-ESTree AST spec", "files": [ "dist", @@ -49,25 +49,17 @@ "targets": { "copy-ast-spec": { "dependsOn": [ - { - "target": "build", - "projects": "dependencies" - } + "^build" ], "outputs": [ "{projectRoot}/src/generated" - ] + ], + "cache": true }, "build": { "dependsOn": [ - { - "target": "build", - "projects": "dependencies" - }, - { - "target": "copy-ast-spec", - "projects": "self" - } + "^build", + "copy-ast-spec" ] } } diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index 059d6a3f5b75..f01e5284b199 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -1,3 +1,27 @@ +## 6.19.0 (2024-01-15) + + +### 🩹 Fixes + +- **typescript-estree:** add JSDocParsingMode enum merge for typescript/lib/tsserverlibrary + +- **typescript-estree:** disallow `using` as the variable keyword for `for..in` loops + +- **typescript-estree:** fix incorrect backwards-compat augmentation in TS 5.3 + + +### ❤️ Thank You + +- auvred +- Brad Zacher +- Josh Goldberg ✨ +- Joshua Chen +- LJX +- Steven +- StyleShit + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 1c476dcaa0cf..ffa4a4049379 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "6.18.1", + "version": "6.19.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "files": [ "dist", @@ -52,8 +52,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 7ebd5cc9e1cb..2fb094c1b589 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -933,6 +933,7 @@ export class Converter { }); case SyntaxKind.ForInStatement: + this.#checkForStatementDeclaration(node.initializer); return this.createNode(node, { type: AST_NODE_TYPES.ForInStatement, left: this.convertPattern(node.initializer), @@ -3510,4 +3511,14 @@ export class Converter { throw createError(message, this.ast, start, end); } + #checkForStatementDeclaration(initializer: ts.ForInitializer): void { + if (ts.isVariableDeclarationList(initializer)) { + if ((initializer.flags & ts.NodeFlags.Using) !== 0) { + this.#throwError( + initializer, + "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.", + ); + } + } + } } diff --git a/packages/typescript-estree/src/parseSettings/index.ts b/packages/typescript-estree/src/parseSettings/index.ts index 6aca7a863aae..1df275901066 100644 --- a/packages/typescript-estree/src/parseSettings/index.ts +++ b/packages/typescript-estree/src/parseSettings/index.ts @@ -12,6 +12,11 @@ declare module 'typescript' { // Added in TypeScript 5.3 enum JSDocParsingMode {} } +// https://github.com/typescript-eslint/typescript-eslint/issues/8172 +declare module 'typescript/lib/tsserverlibrary' { + // Added in TypeScript 5.3 + enum JSDocParsingMode {} +} /** * Internal settings used by the parser to run on a file. diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 08c29892e220..ffa0c4212295 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -244,7 +244,7 @@ function parseAndGenerateServices( options.filePath && parseAndGenerateServicesCalls[options.filePath] > 1 ? createIsolatedProgram(parseSettings) - : getProgramAndAST(parseSettings, hasFullTypeInformation)!; + : getProgramAndAST(parseSettings, hasFullTypeInformation); /** * Convert the TypeScript AST to an ESTree-compatible one, and optionally preserve diff --git a/packages/typescript-estree/src/ts-estree/ts-nodes.ts b/packages/typescript-estree/src/ts-estree/ts-nodes.ts index 5bf6af81a9f5..2a07ee0e7eac 100644 --- a/packages/typescript-estree/src/ts-estree/ts-nodes.ts +++ b/packages/typescript-estree/src/ts-estree/ts-nodes.ts @@ -2,12 +2,11 @@ import type * as ts from 'typescript'; // Workaround to support new TS version features for consumers on old TS versions // Eg: https://github.com/typescript-eslint/typescript-eslint/issues/2388, https://github.com/typescript-eslint/typescript-eslint/issues/2784 -/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/no-empty-interface */ +/* eslint-disable @typescript-eslint/no-empty-interface */ declare module 'typescript' { - /** @ts-ignore - added in TS 4.5, deprecated and converted to a type-alias in TS 5.3 */ - export interface AssertClause extends ts.Node {} - /** @ts-ignore - added in TS 4.5, deprecated and converted to a type-alias in TS 5.3 */ - export interface AssertEntry extends ts.Node {} + // added in TS 4.5, deprecated in TS 5.3 + export interface AssertClause extends ts.ImportAttributes {} + export interface AssertEntry extends ts.ImportAttribute {} // added in TS 4.9 export interface SatisfiesExpression extends ts.Node {} // added in TS 5.1 @@ -16,7 +15,7 @@ declare module 'typescript' { export interface ImportAttribute extends ts.Node {} export interface ImportAttributes extends ts.Node {} } -/* eslint-enable @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/no-empty-interface */ +/* eslint-enable @typescript-eslint/no-empty-interface */ export type TSToken = ts.Token; diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 52ae999b587d..d47292d16a8a 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for utils to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) diff --git a/packages/utils/package.json b/packages/utils/package.json index 7364a438ba3a..e6fd7c529377 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/utils", - "version": "6.18.1", + "version": "6.19.0", "description": "Utilities for working with TypeScript + ESLint together", "files": [ "dist", @@ -68,16 +68,16 @@ "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", "semver": "^7.5.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" }, "devDependencies": { - "@typescript-eslint/parser": "6.18.1", + "@typescript-eslint/parser": "6.19.0", "downlevel-dts": "*", "jest": "29.7.0", "prettier": "^3.0.3", diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md index 00bf29bb06c1..720a04734ade 100644 --- a/packages/visitor-keys/CHANGELOG.md +++ b/packages/visitor-keys/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for visitor-keys to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for visitor-keys to align it with other projects, there were no code changes. diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json index db2f5a50d126..b6444d5107e6 100644 --- a/packages/visitor-keys/package.json +++ b/packages/visitor-keys/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/visitor-keys", - "version": "6.18.1", + "version": "6.19.0", "description": "Visitor keys used to help traverse the TypeScript-ESTree AST", "files": [ "dist", @@ -45,7 +45,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "devDependencies": { diff --git a/packages/website-eslint/CHANGELOG.md b/packages/website-eslint/CHANGELOG.md index 1009bd6da543..eea73823bbc9 100644 --- a/packages/website-eslint/CHANGELOG.md +++ b/packages/website-eslint/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for website-eslint to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for website-eslint to align it with other projects, there were no code changes. diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json index f0049f1b9905..71b4e13a5fbc 100644 --- a/packages/website-eslint/package.json +++ b/packages/website-eslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/website-eslint", - "version": "6.18.1", + "version": "6.19.0", "private": true, "description": "ESLint which works in browsers.", "files": [ @@ -23,16 +23,16 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/utils": "6.18.1" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/utils": "6.19.0" }, "devDependencies": { "@eslint/js": "8.56.0", - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/parser": "6.18.1", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", + "@typescript-eslint/eslint-plugin": "6.19.0", + "@typescript-eslint/parser": "6.19.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "esbuild": "~0.19.0", "eslint": "*", "esquery": "*", diff --git a/packages/website/CHANGELOG.md b/packages/website/CHANGELOG.md index 71f7e0e50972..c647d2d15e38 100644 --- a/packages/website/CHANGELOG.md +++ b/packages/website/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.19.0 (2024-01-15) + +This was a version bump only for website to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 6.18.1 (2024-01-08) This was a version bump only for website to align it with other projects, there were no code changes. diff --git a/packages/website/docusaurusConfig.ts b/packages/website/docusaurusConfig.ts index 625e6008a08f..cc283cfc2993 100644 --- a/packages/website/docusaurusConfig.ts +++ b/packages/website/docusaurusConfig.ts @@ -91,8 +91,8 @@ const themeConfig: AlgoliaThemeConfig & ThemeCommonConfig = { }, { position: 'right', - value: ``, - type: 'html', + href: `https://github.com/typescript-eslint/typescript-eslint/releases/tag/v${version}`, + label: `v${version}`, }, { to: 'play', diff --git a/packages/website/package.json b/packages/website/package.json index bd4b65fe476a..cf6ca13d4a4e 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "website", - "version": "6.18.1", + "version": "6.19.0", "private": true, "scripts": { "build": "docusaurus build", @@ -24,8 +24,8 @@ "@docusaurus/theme-common": "~2.4.1", "@mdx-js/react": "1.6.22", "@prettier/sync": "*", - "@typescript-eslint/parser": "6.18.1", - "@typescript-eslint/website-eslint": "6.18.1", + "@typescript-eslint/parser": "6.19.0", + "@typescript-eslint/website-eslint": "6.19.0", "clsx": "^2.0.0", "eslint": "*", "json-schema": "^0.4.0", @@ -50,9 +50,9 @@ "@types/react": "*", "@types/react-helmet": "^6.1.6", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "6.18.1", - "@typescript-eslint/rule-schema-to-typescript-types": "6.18.1", - "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/eslint-plugin": "6.19.0", + "@typescript-eslint/rule-schema-to-typescript-types": "6.19.0", + "@typescript-eslint/types": "6.19.0", "copy-webpack-plugin": "^11.0.0", "cross-fetch": "*", "globby": "^11.1.0", diff --git a/yarn.lock b/yarn.lock index d2167e10ade3..fc6417562fe4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5221,10 +5221,10 @@ __metadata: resolution: "@typescript-eslint/eslint-plugin-internal@workspace:packages/eslint-plugin-internal" dependencies: "@prettier/sync": "*" - "@typescript-eslint/rule-tester": 6.18.1 - "@typescript-eslint/scope-manager": 6.18.1 - "@typescript-eslint/type-utils": 6.18.1 - "@typescript-eslint/utils": 6.18.1 + "@typescript-eslint/rule-tester": 6.19.0 + "@typescript-eslint/scope-manager": 6.19.0 + "@typescript-eslint/type-utils": 6.19.0 + "@typescript-eslint/utils": 6.19.0 jest: 29.7.0 prettier: ^3.0.3 rimraf: "*" @@ -5236,8 +5236,8 @@ __metadata: resolution: "@typescript-eslint/eslint-plugin-tslint@workspace:packages/eslint-plugin-tslint" dependencies: "@types/lodash": "*" - "@typescript-eslint/parser": 6.18.1 - "@typescript-eslint/utils": 6.18.1 + "@typescript-eslint/parser": 6.19.0 + "@typescript-eslint/utils": 6.19.0 jest: 29.7.0 prettier: ^3.0.3 rimraf: "*" @@ -5248,7 +5248,7 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/eslint-plugin@6.18.1, @typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin": +"@typescript-eslint/eslint-plugin@6.19.0, @typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin": version: 0.0.0-use.local resolution: "@typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin" dependencies: @@ -5257,12 +5257,12 @@ __metadata: "@types/debug": "*" "@types/marked": "*" "@types/natural-compare": "*" - "@typescript-eslint/rule-schema-to-typescript-types": 6.18.1 - "@typescript-eslint/rule-tester": 6.18.1 - "@typescript-eslint/scope-manager": 6.18.1 - "@typescript-eslint/type-utils": 6.18.1 - "@typescript-eslint/utils": 6.18.1 - "@typescript-eslint/visitor-keys": 6.18.1 + "@typescript-eslint/rule-schema-to-typescript-types": 6.19.0 + "@typescript-eslint/rule-tester": 6.19.0 + "@typescript-eslint/scope-manager": 6.19.0 + "@typescript-eslint/type-utils": 6.19.0 + "@typescript-eslint/utils": 6.19.0 + "@typescript-eslint/visitor-keys": 6.19.0 ajv: ^6.12.6 chalk: ^5.3.0 cross-fetch: "*" @@ -5301,15 +5301,15 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/parser@6.18.1, @typescript-eslint/parser@workspace:packages/parser": +"@typescript-eslint/parser@6.19.0, @typescript-eslint/parser@workspace:packages/parser": version: 0.0.0-use.local resolution: "@typescript-eslint/parser@workspace:packages/parser" dependencies: "@types/glob": "*" - "@typescript-eslint/scope-manager": 6.18.1 - "@typescript-eslint/types": 6.18.1 - "@typescript-eslint/typescript-estree": 6.18.1 - "@typescript-eslint/visitor-keys": 6.18.1 + "@typescript-eslint/scope-manager": 6.19.0 + "@typescript-eslint/types": 6.19.0 + "@typescript-eslint/typescript-estree": 6.19.0 + "@typescript-eslint/visitor-keys": 6.19.0 debug: ^4.3.4 downlevel-dts: "*" glob: "*" @@ -5341,26 +5341,26 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/rule-schema-to-typescript-types@6.18.1, @typescript-eslint/rule-schema-to-typescript-types@workspace:packages/rule-schema-to-typescript-types": +"@typescript-eslint/rule-schema-to-typescript-types@6.19.0, @typescript-eslint/rule-schema-to-typescript-types@workspace:packages/rule-schema-to-typescript-types": version: 0.0.0-use.local resolution: "@typescript-eslint/rule-schema-to-typescript-types@workspace:packages/rule-schema-to-typescript-types" dependencies: "@prettier/sync": "*" - "@typescript-eslint/type-utils": 6.18.1 - "@typescript-eslint/utils": 6.18.1 + "@typescript-eslint/type-utils": 6.19.0 + "@typescript-eslint/utils": 6.19.0 natural-compare: ^1.4.0 prettier: ^3.0.3 languageName: unknown linkType: soft -"@typescript-eslint/rule-tester@6.18.1, @typescript-eslint/rule-tester@workspace:packages/rule-tester": +"@typescript-eslint/rule-tester@6.19.0, @typescript-eslint/rule-tester@workspace:packages/rule-tester": version: 0.0.0-use.local resolution: "@typescript-eslint/rule-tester@workspace:packages/rule-tester" dependencies: "@types/lodash.merge": 4.6.9 - "@typescript-eslint/parser": 6.18.1 - "@typescript-eslint/typescript-estree": 6.18.1 - "@typescript-eslint/utils": 6.18.1 + "@typescript-eslint/parser": 6.19.0 + "@typescript-eslint/typescript-estree": 6.19.0 + "@typescript-eslint/utils": 6.19.0 ajv: ^6.10.0 chai: ^4.3.7 lodash.merge: 4.6.2 @@ -5374,15 +5374,15 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/scope-manager@6.18.1, @typescript-eslint/scope-manager@workspace:packages/scope-manager": +"@typescript-eslint/scope-manager@6.19.0, @typescript-eslint/scope-manager@workspace:packages/scope-manager": version: 0.0.0-use.local resolution: "@typescript-eslint/scope-manager@workspace:packages/scope-manager" dependencies: "@prettier/sync": "*" "@types/glob": "*" - "@typescript-eslint/types": 6.18.1 - "@typescript-eslint/typescript-estree": 6.18.1 - "@typescript-eslint/visitor-keys": 6.18.1 + "@typescript-eslint/types": 6.19.0 + "@typescript-eslint/typescript-estree": 6.19.0 + "@typescript-eslint/visitor-keys": 6.19.0 glob: "*" jest-specific-snapshot: "*" make-dir: "*" @@ -5401,13 +5401,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@6.18.1, @typescript-eslint/type-utils@workspace:packages/type-utils": +"@typescript-eslint/type-utils@6.19.0, @typescript-eslint/type-utils@workspace:packages/type-utils": version: 0.0.0-use.local resolution: "@typescript-eslint/type-utils@workspace:packages/type-utils" dependencies: - "@typescript-eslint/parser": 6.18.1 - "@typescript-eslint/typescript-estree": 6.18.1 - "@typescript-eslint/utils": 6.18.1 + "@typescript-eslint/parser": 6.19.0 + "@typescript-eslint/typescript-estree": 6.19.0 + "@typescript-eslint/utils": 6.19.0 ajv: ^6.10.0 debug: ^4.3.4 downlevel-dts: "*" @@ -5424,7 +5424,7 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/types@6.18.1, @typescript-eslint/types@workspace:packages/types": +"@typescript-eslint/types@6.19.0, @typescript-eslint/types@workspace:packages/types": version: 0.0.0-use.local resolution: "@typescript-eslint/types@workspace:packages/types" dependencies: @@ -5515,14 +5515,14 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/typescript-estree@6.18.1, @typescript-eslint/typescript-estree@workspace:packages/typescript-estree": +"@typescript-eslint/typescript-estree@6.19.0, @typescript-eslint/typescript-estree@workspace:packages/typescript-estree": version: 0.0.0-use.local resolution: "@typescript-eslint/typescript-estree@workspace:packages/typescript-estree" dependencies: "@babel/code-frame": "*" "@babel/parser": "*" - "@typescript-eslint/types": 6.18.1 - "@typescript-eslint/visitor-keys": 6.18.1 + "@typescript-eslint/types": 6.19.0 + "@typescript-eslint/visitor-keys": 6.19.0 debug: ^4.3.4 glob: "*" globby: ^11.1.0 @@ -5561,17 +5561,17 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@6.18.1, @typescript-eslint/utils@^6.0.0, @typescript-eslint/utils@workspace:packages/utils": +"@typescript-eslint/utils@6.19.0, @typescript-eslint/utils@^6.0.0, @typescript-eslint/utils@workspace:packages/utils": version: 0.0.0-use.local resolution: "@typescript-eslint/utils@workspace:packages/utils" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/parser": 6.18.1 - "@typescript-eslint/scope-manager": 6.18.1 - "@typescript-eslint/types": 6.18.1 - "@typescript-eslint/typescript-estree": 6.18.1 + "@typescript-eslint/parser": 6.19.0 + "@typescript-eslint/scope-manager": 6.19.0 + "@typescript-eslint/types": 6.19.0 + "@typescript-eslint/typescript-estree": 6.19.0 downlevel-dts: "*" jest: 29.7.0 prettier: ^3.0.3 @@ -5601,12 +5601,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@6.18.1, @typescript-eslint/visitor-keys@workspace:packages/visitor-keys": +"@typescript-eslint/visitor-keys@6.19.0, @typescript-eslint/visitor-keys@workspace:packages/visitor-keys": version: 0.0.0-use.local resolution: "@typescript-eslint/visitor-keys@workspace:packages/visitor-keys" dependencies: "@types/eslint-visitor-keys": "*" - "@typescript-eslint/types": 6.18.1 + "@typescript-eslint/types": 6.19.0 downlevel-dts: "*" eslint-visitor-keys: ^3.4.1 jest: 29.7.0 @@ -5626,18 +5626,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/website-eslint@6.18.1, @typescript-eslint/website-eslint@workspace:packages/website-eslint": +"@typescript-eslint/website-eslint@6.19.0, @typescript-eslint/website-eslint@workspace:packages/website-eslint": version: 0.0.0-use.local resolution: "@typescript-eslint/website-eslint@workspace:packages/website-eslint" dependencies: "@eslint/js": 8.56.0 - "@typescript-eslint/eslint-plugin": 6.18.1 - "@typescript-eslint/parser": 6.18.1 - "@typescript-eslint/scope-manager": 6.18.1 - "@typescript-eslint/types": 6.18.1 - "@typescript-eslint/typescript-estree": 6.18.1 - "@typescript-eslint/utils": 6.18.1 - "@typescript-eslint/visitor-keys": 6.18.1 + "@typescript-eslint/eslint-plugin": 6.19.0 + "@typescript-eslint/parser": 6.19.0 + "@typescript-eslint/scope-manager": 6.19.0 + "@typescript-eslint/types": 6.19.0 + "@typescript-eslint/typescript-estree": 6.19.0 + "@typescript-eslint/utils": 6.19.0 + "@typescript-eslint/visitor-keys": 6.19.0 esbuild: ~0.19.0 eslint: "*" esquery: "*" @@ -18863,11 +18863,11 @@ __metadata: "@types/react": "*" "@types/react-helmet": ^6.1.6 "@types/react-router-dom": ^5.3.3 - "@typescript-eslint/eslint-plugin": 6.18.1 - "@typescript-eslint/parser": 6.18.1 - "@typescript-eslint/rule-schema-to-typescript-types": 6.18.1 - "@typescript-eslint/types": 6.18.1 - "@typescript-eslint/website-eslint": 6.18.1 + "@typescript-eslint/eslint-plugin": 6.19.0 + "@typescript-eslint/parser": 6.19.0 + "@typescript-eslint/rule-schema-to-typescript-types": 6.19.0 + "@typescript-eslint/types": 6.19.0 + "@typescript-eslint/website-eslint": 6.19.0 clsx: ^2.0.0 copy-webpack-plugin: ^11.0.0 cross-fetch: "*"