diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d933da89b0da..356d319a5a9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -261,7 +261,7 @@ jobs: path: coverage - name: Publish code coverage report - uses: codecov/codecov-action@v4.5.0 + uses: codecov/codecov-action@v4.6.0 with: token: ${{ secrets.CODECOV_TOKEN }} files: coverage/**/lcov.info diff --git a/CHANGELOG.md b/CHANGELOG.md index 759f55d4ff18..14d418f1e3c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +## 8.9.0 (2024-10-14) + + +### 🚀 Features + +- **rule-tester:** hooks for test cases ([#10109](https://github.com/typescript-eslint/typescript-eslint/pull/10109)) + +### 🩹 Fixes + +- **eslint-plugin:** [no-unnecessary-type-parameters] cannot assume variables are either type or value ([#10093](https://github.com/typescript-eslint/typescript-eslint/pull/10093)) +- **eslint-plugin:** [return-await] sync the behavior with await-thenable ([#10069](https://github.com/typescript-eslint/typescript-eslint/pull/10069)) +- **eslint-plugin:** [prefer-literal-enum-member] report a different error message when `allowBitwiseExpressions` is enabled ([#10096](https://github.com/typescript-eslint/typescript-eslint/pull/10096)) +- **eslint-plugin:** [no-loop-func] sync from upstream base rule ([#10103](https://github.com/typescript-eslint/typescript-eslint/pull/10103)) +- **eslint-plugin:** [no-unused-vars] never report the naming of an enum member ([#10117](https://github.com/typescript-eslint/typescript-eslint/pull/10117)) +- **eslint-plugin:** correct use-at-your-own-risk type definitions ([#10049](https://github.com/typescript-eslint/typescript-eslint/pull/10049)) +- **eslint-plugin:** handle unions in await...for ([#10110](https://github.com/typescript-eslint/typescript-eslint/pull/10110)) +- **rule-tester:** merge provided `linterOptions` ([#10131](https://github.com/typescript-eslint/typescript-eslint/pull/10131)) +- **scope-manager:** [no-use-before-define] do not treat nested namespace aliases as variable references ([#10095](https://github.com/typescript-eslint/typescript-eslint/pull/10095)) +- **typescript-estree:** improve project service error message when file extension missing from extraFileExtensions ([#10076](https://github.com/typescript-eslint/typescript-eslint/pull/10076)) +- **visitor-keys:** reorder `TSSatisfiesExpression` and `TSTypeAssertion` ([#10139](https://github.com/typescript-eslint/typescript-eslint/pull/10139)) + +### ❤️ Thank You + +- Abraham Guo +- Anna Bocharova @RobinTail +- Arya Emami @aryaemami59 +- auvred @auvred +- Joshua Chen +- Kirk Waiblinger @kirkwaiblinger +- Lotfi Meklati @lotmek +- mdm317 +- Ronen Amiel +- Sukka +- YeonJuan @yeonjuan + +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. + ## 8.8.1 (2024-10-07) diff --git a/eslint.config.mjs b/eslint.config.mjs index 7fc85c328dcd..cc1672c87995 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -66,6 +66,8 @@ export default tseslint.config( '**/build/**', '.nx/*', '.yarn/*', + // Files generated by TypeDoc + 'packages/*/generated', // Files copied as part of the build 'packages/types/src/generated/**/*.ts', // Playground types downloaded from the web @@ -439,7 +441,7 @@ export default tseslint.config( }, }, { - files: ['eslint.config.{js,cjs,mjs}'], + files: ['eslint.config.{js,cjs,mjs}', 'knip.ts', 'packages/*/src/index.ts'], rules: { // requirement 'import/no-default-export': 'off', @@ -596,13 +598,19 @@ export default tseslint.config( }, { extends: [perfectionistPlugin.configs['recommended-alphabetical']], - ignores: ['packages/typescript-eslint/src/configs/*'], + ignores: [ + 'packages/eslint-plugin/src/configs/*', + 'packages/scope-manager/src/configs/*', + 'packages/typescript-eslint/src/configs/*', + ], files: [ 'packages/ast-spec/{src,tests,typings}/**/*.ts', + 'packages/eslint-plugin/{src,tests,tools,typings}/**/*.ts', 'packages/integration-tests/{tests,tools,typing}/**/*.ts', 'packages/parser/{src,tests}/**/*.ts', 'packages/rule-schema-to-typescript-types/src/**/*.ts', 'packages/rule-tester/{src,tests,typings}/**/*.ts', + 'packages/scope-manager/{src,tests}/**/*.ts', 'packages/types/{src,tools}/**/*.ts', 'packages/typescript-eslint/{src,tests}/**/*.ts', 'packages/utils/src/**/*.ts', @@ -611,32 +619,67 @@ export default tseslint.config( ], rules: { '@typescript-eslint/sort-type-constituents': 'off', - 'perfectionist/sort-classes': [ + 'perfectionist/sort-classes': 'error', + 'perfectionist/sort-enums': 'off', + 'perfectionist/sort-objects': 'error', + 'perfectionist/sort-union-types': [ 'error', { - order: 'asc', - partitionByComment: true, + groups: ['unknown', 'keyword', 'nullish'], type: 'natural', }, ], - 'perfectionist/sort-enums': 'off', + 'simple-import-sort/imports': 'off', + }, + settings: { + perfectionist: { + partitionByComment: true, + order: 'asc', + type: 'natural', + }, + }, + }, + { + files: ['packages/ast-spec/src/**/*.ts'], + rules: { + 'perfectionist/sort-interfaces': [ + 'error', + { + customGroups: { + first: ['type'], + }, + groups: ['first', 'unknown'], + }, + ], + }, + }, + { + files: ['packages/eslint-plugin/src/rules/*.ts'], + rules: { 'perfectionist/sort-objects': [ 'error', { - order: 'asc', - partitionByComment: true, - type: 'natural', + customGroups: { + first: ['loc', 'name', 'node', 'type'], + second: ['meta', 'messageId', 'start'], + third: ['defaultOptions', 'data', 'end'], + fourth: ['fix'], + }, + groups: ['first', 'second', 'third', 'fourth', 'unknown'], }, ], - 'perfectionist/sort-union-types': [ + }, + }, + { + files: ['packages/eslint-plugin/tests/rules/*.test.ts'], + rules: { + 'perfectionist/sort-objects': [ 'error', { - order: 'asc', - groups: ['unknown', 'keyword', 'nullish'], - type: 'natural', + customGroups: { top: ['valid'] }, + groups: ['top', 'unknown'], }, ], - 'simple-import-sort/imports': 'off', }, }, ); diff --git a/package.json b/package.json index 3d158d001cea..eaec4491d431 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "eslint-plugin-jest": "^27.9.0", "eslint-plugin-jsdoc": "^47.0.2", "eslint-plugin-jsx-a11y": "^6.8.0", - "eslint-plugin-perfectionist": "^3.2.0", + "eslint-plugin-perfectionist": "^3.8.0", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-regexp": "^2.6.0", @@ -115,7 +115,7 @@ "knip": "^5.9.4", "lint-staged": "^15.2.2", "make-dir": "^4.0.0", - "markdownlint-cli": "^0.41.0", + "markdownlint-cli": "^0.42.0", "ncp": "^2.0.0", "nx": "19.5.2", "prettier": "3.3.2", diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md index 2eb6495e58bc..60096ea98255 100644 --- a/packages/ast-spec/CHANGELOG.md +++ b/packages/ast-spec/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.9.0 (2024-10-14) + +This was a version bump only for ast-spec 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. + ## 8.8.1 (2024-10-07) This was a version bump only for ast-spec to align it with other projects, there were no code changes. diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json index 1dea62fb271f..edf4609cd27c 100644 --- a/packages/ast-spec/package.json +++ b/packages/ast-spec/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/ast-spec", - "version": "8.8.1", + "version": "8.9.0", "description": "Complete specification for the TypeScript-ESTree AST", "private": true, "keywords": [ diff --git a/packages/ast-spec/src/base/LiteralBase.ts b/packages/ast-spec/src/base/LiteralBase.ts index 52d17c496b9b..c878fe7b7aa5 100644 --- a/packages/ast-spec/src/base/LiteralBase.ts +++ b/packages/ast-spec/src/base/LiteralBase.ts @@ -2,7 +2,7 @@ import type { AST_NODE_TYPES } from '../ast-node-types'; import type { BaseNode } from './BaseNode'; export interface LiteralBase extends BaseNode { - raw: string; type: AST_NODE_TYPES.Literal; + raw: string; value: RegExp | bigint | boolean | number | string | null; } diff --git a/packages/ast-spec/src/base/NodeOrTokenData.ts b/packages/ast-spec/src/base/NodeOrTokenData.ts index 3f6e83919bf6..d07163d045b0 100644 --- a/packages/ast-spec/src/base/NodeOrTokenData.ts +++ b/packages/ast-spec/src/base/NodeOrTokenData.ts @@ -2,6 +2,8 @@ import type { Range } from './Range'; import type { SourceLocation } from './SourceLocation'; export interface NodeOrTokenData { + type: string; + /** * The source location information of the node. * @@ -10,6 +12,4 @@ export interface NodeOrTokenData { loc: SourceLocation; range: Range; - - type: string; } diff --git a/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts b/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts index e4dd0c3710f4..5c397c3ceb02 100644 --- a/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts @@ -6,6 +6,7 @@ import type { ImportAttribute } from '../../special/ImportAttribute/spec'; import type { ExportKind } from '../ExportAndImportKind'; export interface ExportAllDeclaration extends BaseNode { + type: AST_NODE_TYPES.ExportAllDeclaration; /** * The assertions declared for the export. * @example @@ -35,5 +36,4 @@ export interface ExportAllDeclaration extends BaseNode { * The source module being exported from. */ source: StringLiteral; - type: AST_NODE_TYPES.ExportAllDeclaration; } diff --git a/packages/ast-spec/src/declaration/ExportDefaultDeclaration/spec.ts b/packages/ast-spec/src/declaration/ExportDefaultDeclaration/spec.ts index b33bdb26098d..d0e750211d6f 100644 --- a/packages/ast-spec/src/declaration/ExportDefaultDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/ExportDefaultDeclaration/spec.ts @@ -3,6 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { DefaultExportDeclarations } from '../../unions/ExportDeclaration'; export interface ExportDefaultDeclaration extends BaseNode { + type: AST_NODE_TYPES.ExportDefaultDeclaration; /** * The declaration being exported. */ @@ -11,5 +12,4 @@ export interface ExportDefaultDeclaration extends BaseNode { * The kind of the export. Always `value` for default exports. */ exportKind: 'value'; - type: AST_NODE_TYPES.ExportDefaultDeclaration; } diff --git a/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts b/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts index 58329b91f309..79c266d9b5fc 100644 --- a/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts @@ -7,6 +7,7 @@ import type { NamedExportDeclarations } from '../../unions/ExportDeclaration'; import type { ExportKind } from '../ExportAndImportKind'; interface ExportNamedDeclarationBase extends BaseNode { + type: AST_NODE_TYPES.ExportNamedDeclaration; /** * The assertions declared for the export. * @example @@ -52,7 +53,6 @@ interface ExportNamedDeclarationBase extends BaseNode { * This will be an empty array if `declaration` is not `null` */ specifiers: ExportSpecifier[]; - type: AST_NODE_TYPES.ExportNamedDeclaration; } /** diff --git a/packages/ast-spec/src/declaration/FunctionDeclaration/spec.ts b/packages/ast-spec/src/declaration/FunctionDeclaration/spec.ts index 2059b331c45c..78264715da0e 100644 --- a/packages/ast-spec/src/declaration/FunctionDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/FunctionDeclaration/spec.ts @@ -4,10 +4,10 @@ import type { Identifier } from '../../expression/Identifier/spec'; import type { BlockStatement } from '../../statement/BlockStatement/spec'; interface FunctionDeclarationBase extends FunctionBase { + type: AST_NODE_TYPES.FunctionDeclaration; body: BlockStatement; declare: false; expression: false; - type: AST_NODE_TYPES.FunctionDeclaration; } /** diff --git a/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts b/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts index e3d200f850f7..6ef26630fbb7 100644 --- a/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts @@ -6,6 +6,7 @@ import type { ImportClause } from '../../unions/ImportClause'; import type { ImportKind } from '../ExportAndImportKind'; export interface ImportDeclaration extends BaseNode { + type: AST_NODE_TYPES.ImportDeclaration; /** * The assertions declared for the export. * @example @@ -43,5 +44,4 @@ export interface ImportDeclaration extends BaseNode { * ``` */ specifiers: ImportClause[]; - type: AST_NODE_TYPES.ImportDeclaration; } diff --git a/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts b/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts index 55dad7af54c8..ab75a908aa87 100644 --- a/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts +++ b/packages/ast-spec/src/declaration/TSDeclareFunction/spec.ts @@ -2,6 +2,7 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { FunctionBase } from '../../base/FunctionBase'; interface TSDeclareFunctionBase extends FunctionBase { + type: AST_NODE_TYPES.TSDeclareFunction; /** * TS1183: An implementation cannot be declared in ambient contexts. */ @@ -11,7 +12,6 @@ interface TSDeclareFunctionBase extends FunctionBase { */ declare: boolean; expression: false; - type: AST_NODE_TYPES.TSDeclareFunction; } /** diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts index 6c818a4655bc..e10a95f7e902 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts @@ -5,6 +5,7 @@ import type { Identifier } from '../../expression/Identifier/spec'; import type { TSEnumBody } from '../../special/TSEnumBody/spec'; export interface TSEnumDeclaration extends BaseNode { + type: AST_NODE_TYPES.TSEnumDeclaration; /** * The body of the enum. */ @@ -34,5 +35,4 @@ export interface TSEnumDeclaration extends BaseNode { * @deprecated Use {@link body} instead. */ members: TSEnumMember[]; - type: AST_NODE_TYPES.TSEnumDeclaration; } diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts index 1ba5e12eff5d..1e4b1d6ca8b0 100644 --- a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts @@ -6,6 +6,7 @@ import type { TSQualifiedName } from '../../type/TSQualifiedName/spec'; import type { ImportKind } from '../ExportAndImportKind'; interface TSImportEqualsDeclarationBase extends BaseNode { + type: AST_NODE_TYPES.TSImportEqualsDeclaration; /** * The locally imported name. */ @@ -25,7 +26,6 @@ interface TSImportEqualsDeclarationBase extends BaseNode { * ``` */ moduleReference: Identifier | TSExternalModuleReference | TSQualifiedName; - type: AST_NODE_TYPES.TSImportEqualsDeclaration; } export interface TSImportEqualsNamespaceDeclaration diff --git a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts index 0b42e52142ad..545160dd34dd 100644 --- a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts @@ -6,6 +6,7 @@ import type { TSInterfaceHeritage } from '../../special/TSInterfaceHeritage/spec import type { TSTypeParameterDeclaration } from '../../special/TSTypeParameterDeclaration/spec'; export interface TSInterfaceDeclaration extends BaseNode { + type: AST_NODE_TYPES.TSInterfaceDeclaration; /** * The body of the interface */ @@ -22,7 +23,6 @@ export interface TSInterfaceDeclaration extends BaseNode { * The name of this interface */ id: Identifier; - type: AST_NODE_TYPES.TSInterfaceDeclaration; /** * The generic type parameters declared for the interface. Empty declaration * (`<>`) is different from no declaration. diff --git a/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts index 6072ae41b071..b17ae830824c 100644 --- a/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts @@ -9,6 +9,7 @@ import type { Literal } from '../../unions/Literal'; export type TSModuleDeclarationKind = 'global' | 'module' | 'namespace'; interface TSModuleDeclarationBase extends BaseNode { + type: AST_NODE_TYPES.TSModuleDeclaration; /** * The body of the module. * This can only be `undefined` for the code `declare module 'mod';` @@ -42,6 +43,7 @@ interface TSModuleDeclarationBase extends BaseNode { * ``` */ id: Identifier | Literal | TSQualifiedName; + /** * The keyword used to define this module declaration * @example @@ -57,8 +59,6 @@ interface TSModuleDeclarationBase extends BaseNode { * ``` */ kind: TSModuleDeclarationKind; - - type: AST_NODE_TYPES.TSModuleDeclaration; } export interface TSModuleDeclarationNamespace extends TSModuleDeclarationBase { diff --git a/packages/ast-spec/src/declaration/TSNamespaceExportDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSNamespaceExportDeclaration/spec.ts index 5b50d9686d1e..183af4e80efc 100644 --- a/packages/ast-spec/src/declaration/TSNamespaceExportDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSNamespaceExportDeclaration/spec.ts @@ -9,9 +9,9 @@ import type { Identifier } from '../../expression/Identifier/spec'; * ``` */ export interface TSNamespaceExportDeclaration extends BaseNode { + type: AST_NODE_TYPES.TSNamespaceExportDeclaration; /** * The name of the global variable that's exported as namespace */ id: Identifier; - type: AST_NODE_TYPES.TSNamespaceExportDeclaration; } diff --git a/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts index 5f52ec5d9016..b2a9d248b73e 100644 --- a/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts @@ -5,6 +5,7 @@ import type { TSTypeParameterDeclaration } from '../../special/TSTypeParameterDe import type { TypeNode } from '../../unions/TypeNode'; export interface TSTypeAliasDeclaration extends BaseNode { + type: AST_NODE_TYPES.TSTypeAliasDeclaration; /** * Whether the type was `declare`d. * @example @@ -17,7 +18,6 @@ export interface TSTypeAliasDeclaration extends BaseNode { * The name of the type. */ id: Identifier; - type: AST_NODE_TYPES.TSTypeAliasDeclaration; /** * The "value" (type) of the declaration */ diff --git a/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts b/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts index 55fca8d49c31..9af4eb89f088 100644 --- a/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts @@ -10,6 +10,7 @@ import type { } from '../../special/VariableDeclarator/spec'; interface LetOrConstOrVarDeclarationBase extends BaseNode { + type: AST_NODE_TYPES.VariableDeclaration; /** * The variables declared by this declaration. * Always non-empty. @@ -38,7 +39,6 @@ interface LetOrConstOrVarDeclarationBase extends BaseNode { * ``` */ kind: 'const' | 'let' | 'var'; - type: AST_NODE_TYPES.VariableDeclaration; } export interface LetOrVarDeclaredDeclaration @@ -91,6 +91,7 @@ export type LetOrConstOrVarDeclaration = | LetOrVarNonDeclaredDeclaration; interface UsingDeclarationBase extends BaseNode { + type: AST_NODE_TYPES.VariableDeclaration; /** * This value will always be `false` * because 'declare' modifier cannot appear on a 'using' declaration. @@ -105,7 +106,6 @@ interface UsingDeclarationBase extends BaseNode { * ``` */ kind: 'await using' | 'using'; - type: AST_NODE_TYPES.VariableDeclaration; } export interface UsingInNormalContextDeclaration extends UsingDeclarationBase { diff --git a/packages/ast-spec/src/element/Property/spec.ts b/packages/ast-spec/src/element/Property/spec.ts index d550d3610507..530a7250ad44 100644 --- a/packages/ast-spec/src/element/Property/spec.ts +++ b/packages/ast-spec/src/element/Property/spec.ts @@ -11,13 +11,13 @@ import type { } from '../../unions/PropertyName'; interface PropertyBase extends BaseNode { + type: AST_NODE_TYPES.Property; computed: boolean; key: PropertyName; kind: 'get' | 'init' | 'set'; method: boolean; optional: boolean; shorthand: boolean; - type: AST_NODE_TYPES.Property; value: | AssignmentPattern | BindingName diff --git a/packages/ast-spec/src/element/SpreadElement/spec.ts b/packages/ast-spec/src/element/SpreadElement/spec.ts index cd48c6dd390f..13a691901710 100644 --- a/packages/ast-spec/src/element/SpreadElement/spec.ts +++ b/packages/ast-spec/src/element/SpreadElement/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface SpreadElement extends BaseNode { - argument: Expression; type: AST_NODE_TYPES.SpreadElement; + argument: Expression; } diff --git a/packages/ast-spec/src/element/StaticBlock/spec.ts b/packages/ast-spec/src/element/StaticBlock/spec.ts index 3af22c439f21..526a5f65f6d8 100644 --- a/packages/ast-spec/src/element/StaticBlock/spec.ts +++ b/packages/ast-spec/src/element/StaticBlock/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Statement } from '../../unions/Statement'; export interface StaticBlock extends BaseNode { - body: Statement[]; type: AST_NODE_TYPES.StaticBlock; + body: Statement[]; } diff --git a/packages/ast-spec/src/element/TSEnumMember/spec.ts b/packages/ast-spec/src/element/TSEnumMember/spec.ts index 78aba458bc22..a80e963ad278 100644 --- a/packages/ast-spec/src/element/TSEnumMember/spec.ts +++ b/packages/ast-spec/src/element/TSEnumMember/spec.ts @@ -7,12 +7,12 @@ import type { } from '../../unions/PropertyName'; interface TSEnumMemberBase extends BaseNode { + type: AST_NODE_TYPES.TSEnumMember; computed: boolean; id: | PropertyNameComputed // this should only happen in semantically invalid code (ts error 1164) | PropertyNameNonComputed; initializer: Expression | undefined; - type: AST_NODE_TYPES.TSEnumMember; } /** diff --git a/packages/ast-spec/src/element/TSIndexSignature/spec.ts b/packages/ast-spec/src/element/TSIndexSignature/spec.ts index 12b49ab2af56..b0910f3da7e0 100644 --- a/packages/ast-spec/src/element/TSIndexSignature/spec.ts +++ b/packages/ast-spec/src/element/TSIndexSignature/spec.ts @@ -5,10 +5,10 @@ import type { TSTypeAnnotation } from '../../special/TSTypeAnnotation/spec'; import type { Parameter } from '../../unions/Parameter'; export interface TSIndexSignature extends BaseNode { + type: AST_NODE_TYPES.TSIndexSignature; accessibility: Accessibility | undefined; parameters: Parameter[]; readonly: boolean; static: boolean; - type: AST_NODE_TYPES.TSIndexSignature; typeAnnotation: TSTypeAnnotation | undefined; } diff --git a/packages/ast-spec/src/element/TSMethodSignature/spec.ts b/packages/ast-spec/src/element/TSMethodSignature/spec.ts index 80a1011625be..d15d83f171ed 100644 --- a/packages/ast-spec/src/element/TSMethodSignature/spec.ts +++ b/packages/ast-spec/src/element/TSMethodSignature/spec.ts @@ -11,6 +11,7 @@ import type { } from '../../unions/PropertyName'; interface TSMethodSignatureBase extends BaseNode { + type: AST_NODE_TYPES.TSMethodSignature; accessibility: Accessibility | undefined; computed: boolean; key: PropertyName; @@ -20,7 +21,6 @@ interface TSMethodSignatureBase extends BaseNode { readonly: boolean; returnType: TSTypeAnnotation | undefined; static: boolean; - type: AST_NODE_TYPES.TSMethodSignature; typeParameters: TSTypeParameterDeclaration | undefined; } diff --git a/packages/ast-spec/src/element/TSPropertySignature/spec.ts b/packages/ast-spec/src/element/TSPropertySignature/spec.ts index 4f4aca808d3b..2863e3d948f2 100644 --- a/packages/ast-spec/src/element/TSPropertySignature/spec.ts +++ b/packages/ast-spec/src/element/TSPropertySignature/spec.ts @@ -9,13 +9,13 @@ import type { } from '../../unions/PropertyName'; interface TSPropertySignatureBase extends BaseNode { + type: AST_NODE_TYPES.TSPropertySignature; accessibility: Accessibility | undefined; computed: boolean; key: PropertyName; optional: boolean; readonly: boolean; static: boolean; - type: AST_NODE_TYPES.TSPropertySignature; typeAnnotation: TSTypeAnnotation | undefined; } diff --git a/packages/ast-spec/src/expression/ArrayExpression/spec.ts b/packages/ast-spec/src/expression/ArrayExpression/spec.ts index 3a904afddcff..3dccf5c6ab8a 100644 --- a/packages/ast-spec/src/expression/ArrayExpression/spec.ts +++ b/packages/ast-spec/src/expression/ArrayExpression/spec.ts @@ -4,9 +4,9 @@ import type { SpreadElement } from '../../element/SpreadElement/spec'; import type { Expression } from '../../unions/Expression'; export interface ArrayExpression extends BaseNode { + type: AST_NODE_TYPES.ArrayExpression; /** * an element will be `null` in the case of a sparse array: `[1, ,3]` */ elements: (Expression | SpreadElement | null)[]; - type: AST_NODE_TYPES.ArrayExpression; } diff --git a/packages/ast-spec/src/expression/ArrowFunctionExpression/spec.ts b/packages/ast-spec/src/expression/ArrowFunctionExpression/spec.ts index f86c2cf1f712..271578f8f01f 100644 --- a/packages/ast-spec/src/expression/ArrowFunctionExpression/spec.ts +++ b/packages/ast-spec/src/expression/ArrowFunctionExpression/spec.ts @@ -7,6 +7,7 @@ import type { Expression } from '../../unions/Expression'; import type { Parameter } from '../../unions/Parameter'; export interface ArrowFunctionExpression extends BaseNode { + type: AST_NODE_TYPES.ArrowFunctionExpression; async: boolean; body: BlockStatement | Expression; expression: boolean; @@ -14,6 +15,5 @@ export interface ArrowFunctionExpression extends BaseNode { id: null; params: Parameter[]; returnType: TSTypeAnnotation | undefined; - type: AST_NODE_TYPES.ArrowFunctionExpression; typeParameters: TSTypeParameterDeclaration | undefined; } diff --git a/packages/ast-spec/src/expression/AssignmentExpression/spec.ts b/packages/ast-spec/src/expression/AssignmentExpression/spec.ts index d4cfeea7ef28..585eaa7f70a2 100644 --- a/packages/ast-spec/src/expression/AssignmentExpression/spec.ts +++ b/packages/ast-spec/src/expression/AssignmentExpression/spec.ts @@ -7,8 +7,8 @@ import type { AssignmentOperatorToText } from './AssignmentOperatorToText'; export * from './AssignmentOperatorToText'; export interface AssignmentExpression extends BaseNode { + type: AST_NODE_TYPES.AssignmentExpression; left: Expression; operator: ValueOf; right: Expression; - type: AST_NODE_TYPES.AssignmentExpression; } diff --git a/packages/ast-spec/src/expression/AwaitExpression/spec.ts b/packages/ast-spec/src/expression/AwaitExpression/spec.ts index 173ee879bd7d..c476c916a335 100644 --- a/packages/ast-spec/src/expression/AwaitExpression/spec.ts +++ b/packages/ast-spec/src/expression/AwaitExpression/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface AwaitExpression extends BaseNode { - argument: Expression; type: AST_NODE_TYPES.AwaitExpression; + argument: Expression; } diff --git a/packages/ast-spec/src/expression/BinaryExpression/spec.ts b/packages/ast-spec/src/expression/BinaryExpression/spec.ts index 9547483403bb..6c262d443064 100644 --- a/packages/ast-spec/src/expression/BinaryExpression/spec.ts +++ b/packages/ast-spec/src/expression/BinaryExpression/spec.ts @@ -8,8 +8,8 @@ import type { BinaryOperatorToText } from './BinaryOperatorToText'; export * from './BinaryOperatorToText'; export interface BinaryExpression extends BaseNode { + type: AST_NODE_TYPES.BinaryExpression; left: Expression | PrivateIdentifier; operator: ValueOf; right: Expression; - type: AST_NODE_TYPES.BinaryExpression; } diff --git a/packages/ast-spec/src/expression/CallExpression/spec.ts b/packages/ast-spec/src/expression/CallExpression/spec.ts index dec2522217a7..ef548890757a 100644 --- a/packages/ast-spec/src/expression/CallExpression/spec.ts +++ b/packages/ast-spec/src/expression/CallExpression/spec.ts @@ -5,9 +5,9 @@ import type { CallExpressionArgument } from '../../unions/CallExpressionArgument import type { Expression } from '../../unions/Expression'; export interface CallExpression extends BaseNode { + type: AST_NODE_TYPES.CallExpression; arguments: CallExpressionArgument[]; callee: Expression; optional: boolean; - type: AST_NODE_TYPES.CallExpression; typeArguments: TSTypeParameterInstantiation | undefined; } diff --git a/packages/ast-spec/src/expression/ChainExpression/spec.ts b/packages/ast-spec/src/expression/ChainExpression/spec.ts index fca341de2295..dfad50f3580f 100644 --- a/packages/ast-spec/src/expression/ChainExpression/spec.ts +++ b/packages/ast-spec/src/expression/ChainExpression/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { ChainElement } from '../../unions/ChainElement'; export interface ChainExpression extends BaseNode { - expression: ChainElement; type: AST_NODE_TYPES.ChainExpression; + expression: ChainElement; } diff --git a/packages/ast-spec/src/expression/ClassExpression/spec.ts b/packages/ast-spec/src/expression/ClassExpression/spec.ts index 0f436394aa07..71a7be13140a 100644 --- a/packages/ast-spec/src/expression/ClassExpression/spec.ts +++ b/packages/ast-spec/src/expression/ClassExpression/spec.ts @@ -2,7 +2,7 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { ClassBase } from '../../base/ClassBase'; export interface ClassExpression extends ClassBase { + type: AST_NODE_TYPES.ClassExpression; abstract: false; declare: false; - type: AST_NODE_TYPES.ClassExpression; } diff --git a/packages/ast-spec/src/expression/ConditionalExpression/spec.ts b/packages/ast-spec/src/expression/ConditionalExpression/spec.ts index a4a6e9cba4d7..6440dafa7b40 100644 --- a/packages/ast-spec/src/expression/ConditionalExpression/spec.ts +++ b/packages/ast-spec/src/expression/ConditionalExpression/spec.ts @@ -3,8 +3,8 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface ConditionalExpression extends BaseNode { + type: AST_NODE_TYPES.ConditionalExpression; alternate: Expression; consequent: Expression; test: Expression; - type: AST_NODE_TYPES.ConditionalExpression; } diff --git a/packages/ast-spec/src/expression/FunctionExpression/spec.ts b/packages/ast-spec/src/expression/FunctionExpression/spec.ts index 79057f2061e1..41f592a972f0 100644 --- a/packages/ast-spec/src/expression/FunctionExpression/spec.ts +++ b/packages/ast-spec/src/expression/FunctionExpression/spec.ts @@ -3,7 +3,7 @@ import type { FunctionBase } from '../../base/FunctionBase'; import type { BlockStatement } from '../../statement/BlockStatement/spec'; export interface FunctionExpression extends FunctionBase { + type: AST_NODE_TYPES.FunctionExpression; body: BlockStatement; expression: false; - type: AST_NODE_TYPES.FunctionExpression; } diff --git a/packages/ast-spec/src/expression/Identifier/spec.ts b/packages/ast-spec/src/expression/Identifier/spec.ts index b7cce6aa6706..1a127d43fc90 100644 --- a/packages/ast-spec/src/expression/Identifier/spec.ts +++ b/packages/ast-spec/src/expression/Identifier/spec.ts @@ -4,9 +4,9 @@ import type { Decorator } from '../../special/Decorator/spec'; import type { TSTypeAnnotation } from '../../special/TSTypeAnnotation/spec'; export interface Identifier extends BaseNode { + type: AST_NODE_TYPES.Identifier; decorators: Decorator[]; name: string; optional: boolean; - type: AST_NODE_TYPES.Identifier; typeAnnotation: TSTypeAnnotation | undefined; } diff --git a/packages/ast-spec/src/expression/ImportExpression/spec.ts b/packages/ast-spec/src/expression/ImportExpression/spec.ts index f6f95e097dd3..9365bdae6265 100644 --- a/packages/ast-spec/src/expression/ImportExpression/spec.ts +++ b/packages/ast-spec/src/expression/ImportExpression/spec.ts @@ -3,7 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface ImportExpression extends BaseNode { + type: AST_NODE_TYPES.ImportExpression; attributes: Expression | null; source: Expression; - type: AST_NODE_TYPES.ImportExpression; } diff --git a/packages/ast-spec/src/expression/JSXElement/spec.ts b/packages/ast-spec/src/expression/JSXElement/spec.ts index 50d2b58f5b65..134b17d24124 100644 --- a/packages/ast-spec/src/expression/JSXElement/spec.ts +++ b/packages/ast-spec/src/expression/JSXElement/spec.ts @@ -5,8 +5,8 @@ import type { JSXOpeningElement } from '../../jsx/JSXOpeningElement/spec'; import type { JSXChild } from '../../unions/JSXChild'; export interface JSXElement extends BaseNode { + type: AST_NODE_TYPES.JSXElement; children: JSXChild[]; closingElement: JSXClosingElement | null; openingElement: JSXOpeningElement; - type: AST_NODE_TYPES.JSXElement; } diff --git a/packages/ast-spec/src/expression/JSXFragment/spec.ts b/packages/ast-spec/src/expression/JSXFragment/spec.ts index 2ef1068d56df..6497a63e5007 100644 --- a/packages/ast-spec/src/expression/JSXFragment/spec.ts +++ b/packages/ast-spec/src/expression/JSXFragment/spec.ts @@ -5,8 +5,8 @@ import type { JSXOpeningFragment } from '../../jsx/JSXOpeningFragment/spec'; import type { JSXChild } from '../../unions/JSXChild'; export interface JSXFragment extends BaseNode { + type: AST_NODE_TYPES.JSXFragment; children: JSXChild[]; closingFragment: JSXClosingFragment; openingFragment: JSXOpeningFragment; - type: AST_NODE_TYPES.JSXFragment; } diff --git a/packages/ast-spec/src/expression/LogicalExpression/spec.ts b/packages/ast-spec/src/expression/LogicalExpression/spec.ts index 3e7d06b29d1c..c217c469092e 100644 --- a/packages/ast-spec/src/expression/LogicalExpression/spec.ts +++ b/packages/ast-spec/src/expression/LogicalExpression/spec.ts @@ -3,8 +3,8 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface LogicalExpression extends BaseNode { + type: AST_NODE_TYPES.LogicalExpression; left: Expression; operator: '&&' | '??' | '||'; right: Expression; - type: AST_NODE_TYPES.LogicalExpression; } diff --git a/packages/ast-spec/src/expression/MemberExpression/spec.ts b/packages/ast-spec/src/expression/MemberExpression/spec.ts index f4e9f2f3e0a0..aa9e2ce75ad8 100644 --- a/packages/ast-spec/src/expression/MemberExpression/spec.ts +++ b/packages/ast-spec/src/expression/MemberExpression/spec.ts @@ -12,15 +12,15 @@ interface MemberExpressionBase extends BaseNode { } export interface MemberExpressionComputedName extends MemberExpressionBase { + type: AST_NODE_TYPES.MemberExpression; computed: true; property: Expression; - type: AST_NODE_TYPES.MemberExpression; } export interface MemberExpressionNonComputedName extends MemberExpressionBase { + type: AST_NODE_TYPES.MemberExpression; computed: false; property: Identifier | PrivateIdentifier; - type: AST_NODE_TYPES.MemberExpression; } export type MemberExpression = diff --git a/packages/ast-spec/src/expression/MetaProperty/spec.ts b/packages/ast-spec/src/expression/MetaProperty/spec.ts index 18e8d84068ab..5bc9afb81113 100644 --- a/packages/ast-spec/src/expression/MetaProperty/spec.ts +++ b/packages/ast-spec/src/expression/MetaProperty/spec.ts @@ -3,7 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Identifier } from '../Identifier/spec'; export interface MetaProperty extends BaseNode { + type: AST_NODE_TYPES.MetaProperty; meta: Identifier; property: Identifier; - type: AST_NODE_TYPES.MetaProperty; } diff --git a/packages/ast-spec/src/expression/NewExpression/spec.ts b/packages/ast-spec/src/expression/NewExpression/spec.ts index adc069341bb5..99187a809d6f 100644 --- a/packages/ast-spec/src/expression/NewExpression/spec.ts +++ b/packages/ast-spec/src/expression/NewExpression/spec.ts @@ -5,8 +5,8 @@ import type { CallExpressionArgument } from '../../unions/CallExpressionArgument import type { Expression } from '../../unions/Expression'; export interface NewExpression extends BaseNode { + type: AST_NODE_TYPES.NewExpression; arguments: CallExpressionArgument[]; callee: Expression; - type: AST_NODE_TYPES.NewExpression; typeArguments: TSTypeParameterInstantiation | undefined; } diff --git a/packages/ast-spec/src/expression/ObjectExpression/spec.ts b/packages/ast-spec/src/expression/ObjectExpression/spec.ts index a96f0fac5350..0573a2a76faf 100644 --- a/packages/ast-spec/src/expression/ObjectExpression/spec.ts +++ b/packages/ast-spec/src/expression/ObjectExpression/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { ObjectLiteralElement } from '../../unions/ObjectLiteralElement'; export interface ObjectExpression extends BaseNode { - properties: ObjectLiteralElement[]; type: AST_NODE_TYPES.ObjectExpression; + properties: ObjectLiteralElement[]; } diff --git a/packages/ast-spec/src/expression/SequenceExpression/spec.ts b/packages/ast-spec/src/expression/SequenceExpression/spec.ts index a63ed2ecf29f..fa571adb4f08 100644 --- a/packages/ast-spec/src/expression/SequenceExpression/spec.ts +++ b/packages/ast-spec/src/expression/SequenceExpression/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface SequenceExpression extends BaseNode { - expressions: Expression[]; type: AST_NODE_TYPES.SequenceExpression; + expressions: Expression[]; } diff --git a/packages/ast-spec/src/expression/TSAsExpression/spec.ts b/packages/ast-spec/src/expression/TSAsExpression/spec.ts index 669f2339e2c9..b90925a53ca7 100644 --- a/packages/ast-spec/src/expression/TSAsExpression/spec.ts +++ b/packages/ast-spec/src/expression/TSAsExpression/spec.ts @@ -4,7 +4,7 @@ import type { Expression } from '../../unions/Expression'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSAsExpression extends BaseNode { - expression: Expression; type: AST_NODE_TYPES.TSAsExpression; + expression: Expression; typeAnnotation: TypeNode; } diff --git a/packages/ast-spec/src/expression/TSEmptyBodyFunctionExpression/spec.ts b/packages/ast-spec/src/expression/TSEmptyBodyFunctionExpression/spec.ts index ad637d21d35c..77c8779c7d14 100644 --- a/packages/ast-spec/src/expression/TSEmptyBodyFunctionExpression/spec.ts +++ b/packages/ast-spec/src/expression/TSEmptyBodyFunctionExpression/spec.ts @@ -2,7 +2,7 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { FunctionBase } from '../../base/FunctionBase'; export interface TSEmptyBodyFunctionExpression extends FunctionBase { + type: AST_NODE_TYPES.TSEmptyBodyFunctionExpression; body: null; id: null; - type: AST_NODE_TYPES.TSEmptyBodyFunctionExpression; } diff --git a/packages/ast-spec/src/expression/TSInstantiationExpression/spec.ts b/packages/ast-spec/src/expression/TSInstantiationExpression/spec.ts index 3219cb8657fb..2219a440bdf7 100644 --- a/packages/ast-spec/src/expression/TSInstantiationExpression/spec.ts +++ b/packages/ast-spec/src/expression/TSInstantiationExpression/spec.ts @@ -4,7 +4,7 @@ import type { TSTypeParameterInstantiation } from '../../special/spec'; import type { Expression } from '../../unions/Expression'; export interface TSInstantiationExpression extends BaseNode { - expression: Expression; type: AST_NODE_TYPES.TSInstantiationExpression; + expression: Expression; typeArguments: TSTypeParameterInstantiation; } diff --git a/packages/ast-spec/src/expression/TSNonNullExpression/spec.ts b/packages/ast-spec/src/expression/TSNonNullExpression/spec.ts index 19e7187192f8..fd25d33d372f 100644 --- a/packages/ast-spec/src/expression/TSNonNullExpression/spec.ts +++ b/packages/ast-spec/src/expression/TSNonNullExpression/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface TSNonNullExpression extends BaseNode { - expression: Expression; type: AST_NODE_TYPES.TSNonNullExpression; + expression: Expression; } diff --git a/packages/ast-spec/src/expression/TSSatisfiesExpression/spec.ts b/packages/ast-spec/src/expression/TSSatisfiesExpression/spec.ts index 39e1c59f8ffe..1297ade3eba8 100644 --- a/packages/ast-spec/src/expression/TSSatisfiesExpression/spec.ts +++ b/packages/ast-spec/src/expression/TSSatisfiesExpression/spec.ts @@ -4,7 +4,7 @@ import type { Expression } from '../../unions/Expression'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSSatisfiesExpression extends BaseNode { - expression: Expression; type: AST_NODE_TYPES.TSSatisfiesExpression; + expression: Expression; typeAnnotation: TypeNode; } diff --git a/packages/ast-spec/src/expression/TSTypeAssertion/spec.ts b/packages/ast-spec/src/expression/TSTypeAssertion/spec.ts index a410c7f78ddc..308b540de5ed 100644 --- a/packages/ast-spec/src/expression/TSTypeAssertion/spec.ts +++ b/packages/ast-spec/src/expression/TSTypeAssertion/spec.ts @@ -4,7 +4,7 @@ import type { Expression } from '../../unions/Expression'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSTypeAssertion extends BaseNode { - expression: Expression; type: AST_NODE_TYPES.TSTypeAssertion; + expression: Expression; typeAnnotation: TypeNode; } diff --git a/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts b/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts index 2f9c3fbb2882..3000f2ec94a3 100644 --- a/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts +++ b/packages/ast-spec/src/expression/TaggedTemplateExpression/spec.ts @@ -5,8 +5,8 @@ import type { Expression } from '../../unions/Expression'; import type { TemplateLiteral } from '../TemplateLiteral/spec'; export interface TaggedTemplateExpression extends BaseNode { + type: AST_NODE_TYPES.TaggedTemplateExpression; quasi: TemplateLiteral; tag: Expression; - type: AST_NODE_TYPES.TaggedTemplateExpression; typeArguments: TSTypeParameterInstantiation | undefined; } diff --git a/packages/ast-spec/src/expression/TemplateLiteral/spec.ts b/packages/ast-spec/src/expression/TemplateLiteral/spec.ts index 4d2c031313de..8743ed1190ac 100644 --- a/packages/ast-spec/src/expression/TemplateLiteral/spec.ts +++ b/packages/ast-spec/src/expression/TemplateLiteral/spec.ts @@ -4,7 +4,7 @@ import type { TemplateElement } from '../../special/TemplateElement/spec'; import type { Expression } from '../../unions/Expression'; export interface TemplateLiteral extends BaseNode { + type: AST_NODE_TYPES.TemplateLiteral; expressions: Expression[]; quasis: TemplateElement[]; - type: AST_NODE_TYPES.TemplateLiteral; } diff --git a/packages/ast-spec/src/expression/UnaryExpression/spec.ts b/packages/ast-spec/src/expression/UnaryExpression/spec.ts index db16f93b69fd..2d9f09f317b8 100644 --- a/packages/ast-spec/src/expression/UnaryExpression/spec.ts +++ b/packages/ast-spec/src/expression/UnaryExpression/spec.ts @@ -2,6 +2,6 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { UnaryExpressionBase } from '../../base/UnaryExpressionBase'; export interface UnaryExpression extends UnaryExpressionBase { - operator: '!' | '+' | '~' | '-' | 'delete' | 'typeof' | 'void'; type: AST_NODE_TYPES.UnaryExpression; + operator: '!' | '+' | '~' | '-' | 'delete' | 'typeof' | 'void'; } diff --git a/packages/ast-spec/src/expression/UpdateExpression/spec.ts b/packages/ast-spec/src/expression/UpdateExpression/spec.ts index 2a5b8466c7d3..fd98251e44ea 100644 --- a/packages/ast-spec/src/expression/UpdateExpression/spec.ts +++ b/packages/ast-spec/src/expression/UpdateExpression/spec.ts @@ -2,6 +2,6 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { UnaryExpressionBase } from '../../base/UnaryExpressionBase'; export interface UpdateExpression extends UnaryExpressionBase { - operator: '++' | '--'; type: AST_NODE_TYPES.UpdateExpression; + operator: '++' | '--'; } diff --git a/packages/ast-spec/src/expression/YieldExpression/spec.ts b/packages/ast-spec/src/expression/YieldExpression/spec.ts index 5c219c6d8e0d..c88f40e52744 100644 --- a/packages/ast-spec/src/expression/YieldExpression/spec.ts +++ b/packages/ast-spec/src/expression/YieldExpression/spec.ts @@ -3,7 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface YieldExpression extends BaseNode { + type: AST_NODE_TYPES.YieldExpression; argument: Expression | undefined; delegate: boolean; - type: AST_NODE_TYPES.YieldExpression; } diff --git a/packages/ast-spec/src/jsx/JSXAttribute/spec.ts b/packages/ast-spec/src/jsx/JSXAttribute/spec.ts index 0749db9b4356..c8e52b244578 100644 --- a/packages/ast-spec/src/jsx/JSXAttribute/spec.ts +++ b/packages/ast-spec/src/jsx/JSXAttribute/spec.ts @@ -7,7 +7,7 @@ import type { JSXIdentifier } from '../JSXIdentifier/spec'; import type { JSXNamespacedName } from '../JSXNamespacedName/spec'; export interface JSXAttribute extends BaseNode { - name: JSXIdentifier | JSXNamespacedName; type: AST_NODE_TYPES.JSXAttribute; + name: JSXIdentifier | JSXNamespacedName; value: JSXElement | JSXExpression | Literal | null; } diff --git a/packages/ast-spec/src/jsx/JSXClosingElement/spec.ts b/packages/ast-spec/src/jsx/JSXClosingElement/spec.ts index 01135281cccb..ea698d6059ed 100644 --- a/packages/ast-spec/src/jsx/JSXClosingElement/spec.ts +++ b/packages/ast-spec/src/jsx/JSXClosingElement/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { JSXTagNameExpression } from '../../unions/JSXTagNameExpression'; export interface JSXClosingElement extends BaseNode { - name: JSXTagNameExpression; type: AST_NODE_TYPES.JSXClosingElement; + name: JSXTagNameExpression; } diff --git a/packages/ast-spec/src/jsx/JSXExpressionContainer/spec.ts b/packages/ast-spec/src/jsx/JSXExpressionContainer/spec.ts index 510410bf083a..1a0673e6fd15 100644 --- a/packages/ast-spec/src/jsx/JSXExpressionContainer/spec.ts +++ b/packages/ast-spec/src/jsx/JSXExpressionContainer/spec.ts @@ -4,6 +4,6 @@ import type { Expression } from '../../unions/Expression'; import type { JSXEmptyExpression } from '../JSXEmptyExpression/spec'; export interface JSXExpressionContainer extends BaseNode { - expression: Expression | JSXEmptyExpression; type: AST_NODE_TYPES.JSXExpressionContainer; + expression: Expression | JSXEmptyExpression; } diff --git a/packages/ast-spec/src/jsx/JSXIdentifier/spec.ts b/packages/ast-spec/src/jsx/JSXIdentifier/spec.ts index 622cd8a7a758..1d7b71d67ab0 100644 --- a/packages/ast-spec/src/jsx/JSXIdentifier/spec.ts +++ b/packages/ast-spec/src/jsx/JSXIdentifier/spec.ts @@ -2,6 +2,6 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; export interface JSXIdentifier extends BaseNode { - name: string; type: AST_NODE_TYPES.JSXIdentifier; + name: string; } diff --git a/packages/ast-spec/src/jsx/JSXMemberExpression/spec.ts b/packages/ast-spec/src/jsx/JSXMemberExpression/spec.ts index 169607be6e77..e0cda9c16ee4 100644 --- a/packages/ast-spec/src/jsx/JSXMemberExpression/spec.ts +++ b/packages/ast-spec/src/jsx/JSXMemberExpression/spec.ts @@ -4,7 +4,7 @@ import type { JSXTagNameExpression } from '../../unions/JSXTagNameExpression'; import type { JSXIdentifier } from '../JSXIdentifier/spec'; export interface JSXMemberExpression extends BaseNode { + type: AST_NODE_TYPES.JSXMemberExpression; object: JSXTagNameExpression; property: JSXIdentifier; - type: AST_NODE_TYPES.JSXMemberExpression; } diff --git a/packages/ast-spec/src/jsx/JSXNamespacedName/spec.ts b/packages/ast-spec/src/jsx/JSXNamespacedName/spec.ts index 446b426c7f69..0cc97258d9cd 100644 --- a/packages/ast-spec/src/jsx/JSXNamespacedName/spec.ts +++ b/packages/ast-spec/src/jsx/JSXNamespacedName/spec.ts @@ -3,7 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { JSXIdentifier } from '../JSXIdentifier/spec'; export interface JSXNamespacedName extends BaseNode { + type: AST_NODE_TYPES.JSXNamespacedName; name: JSXIdentifier; namespace: JSXIdentifier; - type: AST_NODE_TYPES.JSXNamespacedName; } diff --git a/packages/ast-spec/src/jsx/JSXOpeningElement/spec.ts b/packages/ast-spec/src/jsx/JSXOpeningElement/spec.ts index 6e6bbb7564be..5e2ecf37ce19 100644 --- a/packages/ast-spec/src/jsx/JSXOpeningElement/spec.ts +++ b/packages/ast-spec/src/jsx/JSXOpeningElement/spec.ts @@ -6,9 +6,9 @@ import type { JSXAttribute } from '../JSXAttribute/spec'; import type { JSXSpreadAttribute } from '../JSXSpreadAttribute/spec'; export interface JSXOpeningElement extends BaseNode { + type: AST_NODE_TYPES.JSXOpeningElement; attributes: (JSXAttribute | JSXSpreadAttribute)[]; name: JSXTagNameExpression; selfClosing: boolean; - type: AST_NODE_TYPES.JSXOpeningElement; typeArguments: TSTypeParameterInstantiation | undefined; } diff --git a/packages/ast-spec/src/jsx/JSXSpreadAttribute/spec.ts b/packages/ast-spec/src/jsx/JSXSpreadAttribute/spec.ts index e412aab4e068..db6e6fc1d1bc 100644 --- a/packages/ast-spec/src/jsx/JSXSpreadAttribute/spec.ts +++ b/packages/ast-spec/src/jsx/JSXSpreadAttribute/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface JSXSpreadAttribute extends BaseNode { - argument: Expression; type: AST_NODE_TYPES.JSXSpreadAttribute; + argument: Expression; } diff --git a/packages/ast-spec/src/jsx/JSXSpreadChild/spec.ts b/packages/ast-spec/src/jsx/JSXSpreadChild/spec.ts index 4ee9b2e4cf4a..53fe53555c30 100644 --- a/packages/ast-spec/src/jsx/JSXSpreadChild/spec.ts +++ b/packages/ast-spec/src/jsx/JSXSpreadChild/spec.ts @@ -4,6 +4,6 @@ import type { Expression } from '../../unions/Expression'; import type { JSXEmptyExpression } from '../JSXEmptyExpression/spec'; export interface JSXSpreadChild extends BaseNode { - expression: Expression | JSXEmptyExpression; type: AST_NODE_TYPES.JSXSpreadChild; + expression: Expression | JSXEmptyExpression; } diff --git a/packages/ast-spec/src/jsx/JSXText/spec.ts b/packages/ast-spec/src/jsx/JSXText/spec.ts index fa4a354fff48..d482e30ce93f 100644 --- a/packages/ast-spec/src/jsx/JSXText/spec.ts +++ b/packages/ast-spec/src/jsx/JSXText/spec.ts @@ -2,7 +2,7 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; export interface JSXText extends BaseNode { - raw: string; type: AST_NODE_TYPES.JSXText; + raw: string; value: string; } diff --git a/packages/ast-spec/src/parameter/ArrayPattern/spec.ts b/packages/ast-spec/src/parameter/ArrayPattern/spec.ts index 930ca91acd21..54c117f83d1d 100644 --- a/packages/ast-spec/src/parameter/ArrayPattern/spec.ts +++ b/packages/ast-spec/src/parameter/ArrayPattern/spec.ts @@ -5,9 +5,9 @@ import type { TSTypeAnnotation } from '../../special/TSTypeAnnotation/spec'; import type { DestructuringPattern } from '../../unions/DestructuringPattern'; export interface ArrayPattern extends BaseNode { + type: AST_NODE_TYPES.ArrayPattern; decorators: Decorator[]; elements: (DestructuringPattern | null)[]; optional: boolean; - type: AST_NODE_TYPES.ArrayPattern; typeAnnotation: TSTypeAnnotation | undefined; } diff --git a/packages/ast-spec/src/parameter/AssignmentPattern/spec.ts b/packages/ast-spec/src/parameter/AssignmentPattern/spec.ts index aefcb6dd540e..1afc87567756 100644 --- a/packages/ast-spec/src/parameter/AssignmentPattern/spec.ts +++ b/packages/ast-spec/src/parameter/AssignmentPattern/spec.ts @@ -6,10 +6,10 @@ import type { BindingName } from '../../unions/BindingName'; import type { Expression } from '../../unions/Expression'; export interface AssignmentPattern extends BaseNode { + type: AST_NODE_TYPES.AssignmentPattern; decorators: Decorator[]; left: BindingName; optional: boolean; right: Expression; - type: AST_NODE_TYPES.AssignmentPattern; typeAnnotation: TSTypeAnnotation | undefined; } diff --git a/packages/ast-spec/src/parameter/ObjectPattern/spec.ts b/packages/ast-spec/src/parameter/ObjectPattern/spec.ts index 2b7219b7bc79..77835f916be8 100644 --- a/packages/ast-spec/src/parameter/ObjectPattern/spec.ts +++ b/packages/ast-spec/src/parameter/ObjectPattern/spec.ts @@ -6,9 +6,9 @@ import type { TSTypeAnnotation } from '../../special/TSTypeAnnotation/spec'; import type { RestElement } from '../RestElement/spec'; export interface ObjectPattern extends BaseNode { + type: AST_NODE_TYPES.ObjectPattern; decorators: Decorator[]; optional: boolean; properties: (Property | RestElement)[]; - type: AST_NODE_TYPES.ObjectPattern; typeAnnotation: TSTypeAnnotation | undefined; } diff --git a/packages/ast-spec/src/parameter/RestElement/spec.ts b/packages/ast-spec/src/parameter/RestElement/spec.ts index ed2ad1254b32..786004e6c65a 100644 --- a/packages/ast-spec/src/parameter/RestElement/spec.ts +++ b/packages/ast-spec/src/parameter/RestElement/spec.ts @@ -6,10 +6,10 @@ import type { DestructuringPattern } from '../../unions/DestructuringPattern'; import type { AssignmentPattern } from '../AssignmentPattern/spec'; export interface RestElement extends BaseNode { + type: AST_NODE_TYPES.RestElement; argument: DestructuringPattern; decorators: Decorator[]; optional: boolean; - type: AST_NODE_TYPES.RestElement; typeAnnotation: TSTypeAnnotation | undefined; value: AssignmentPattern | undefined; } diff --git a/packages/ast-spec/src/parameter/TSParameterProperty/spec.ts b/packages/ast-spec/src/parameter/TSParameterProperty/spec.ts index 38290e391720..2471c9985dcb 100644 --- a/packages/ast-spec/src/parameter/TSParameterProperty/spec.ts +++ b/packages/ast-spec/src/parameter/TSParameterProperty/spec.ts @@ -7,11 +7,11 @@ import type { AssignmentPattern } from '../AssignmentPattern/spec'; import type { RestElement } from '../RestElement/spec'; export interface TSParameterProperty extends BaseNode { + type: AST_NODE_TYPES.TSParameterProperty; accessibility: Accessibility | undefined; decorators: Decorator[]; override: boolean; parameter: AssignmentPattern | BindingName | RestElement; readonly: boolean; static: boolean; - type: AST_NODE_TYPES.TSParameterProperty; } diff --git a/packages/ast-spec/src/special/CatchClause/spec.ts b/packages/ast-spec/src/special/CatchClause/spec.ts index e083e7cd308d..a7c4d233d0ff 100644 --- a/packages/ast-spec/src/special/CatchClause/spec.ts +++ b/packages/ast-spec/src/special/CatchClause/spec.ts @@ -4,7 +4,7 @@ import type { BlockStatement } from '../../statement/BlockStatement/spec'; import type { BindingName } from '../../unions/BindingName'; export interface CatchClause extends BaseNode { + type: AST_NODE_TYPES.CatchClause; body: BlockStatement; param: BindingName | null; - type: AST_NODE_TYPES.CatchClause; } diff --git a/packages/ast-spec/src/special/ClassBody/spec.ts b/packages/ast-spec/src/special/ClassBody/spec.ts index 92ab12ba3a21..11c93d540fb3 100644 --- a/packages/ast-spec/src/special/ClassBody/spec.ts +++ b/packages/ast-spec/src/special/ClassBody/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { ClassElement } from '../../unions/ClassElement'; export interface ClassBody extends BaseNode { - body: ClassElement[]; type: AST_NODE_TYPES.ClassBody; + body: ClassElement[]; } diff --git a/packages/ast-spec/src/special/Decorator/spec.ts b/packages/ast-spec/src/special/Decorator/spec.ts index 26e4c85c221c..3c8d1e819042 100644 --- a/packages/ast-spec/src/special/Decorator/spec.ts +++ b/packages/ast-spec/src/special/Decorator/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { LeftHandSideExpression } from '../../unions/LeftHandSideExpression'; export interface Decorator extends BaseNode { - expression: LeftHandSideExpression; type: AST_NODE_TYPES.Decorator; + expression: LeftHandSideExpression; } diff --git a/packages/ast-spec/src/special/ExportSpecifier/spec.ts b/packages/ast-spec/src/special/ExportSpecifier/spec.ts index 0e2f93eb1f3c..cac5c06c2e43 100644 --- a/packages/ast-spec/src/special/ExportSpecifier/spec.ts +++ b/packages/ast-spec/src/special/ExportSpecifier/spec.ts @@ -4,8 +4,8 @@ import type { ExportKind } from '../../declaration/ExportAndImportKind'; import type { Identifier } from '../../expression/Identifier/spec'; export interface ExportSpecifier extends BaseNode { + type: AST_NODE_TYPES.ExportSpecifier; exported: Identifier; exportKind: ExportKind; local: Identifier; - type: AST_NODE_TYPES.ExportSpecifier; } diff --git a/packages/ast-spec/src/special/ImportAttribute/spec.ts b/packages/ast-spec/src/special/ImportAttribute/spec.ts index 3001b2d08890..988e31bb8585 100644 --- a/packages/ast-spec/src/special/ImportAttribute/spec.ts +++ b/packages/ast-spec/src/special/ImportAttribute/spec.ts @@ -4,7 +4,7 @@ import type { Identifier } from '../../expression/Identifier/spec'; import type { Literal } from '../../unions/Literal'; export interface ImportAttribute extends BaseNode { - key: Identifier | Literal; type: AST_NODE_TYPES.ImportAttribute; + key: Identifier | Literal; value: Literal; } diff --git a/packages/ast-spec/src/special/ImportDefaultSpecifier/spec.ts b/packages/ast-spec/src/special/ImportDefaultSpecifier/spec.ts index 0a68010c0008..c4ad22f20340 100644 --- a/packages/ast-spec/src/special/ImportDefaultSpecifier/spec.ts +++ b/packages/ast-spec/src/special/ImportDefaultSpecifier/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Identifier } from '../../expression/Identifier/spec'; export interface ImportDefaultSpecifier extends BaseNode { - local: Identifier; type: AST_NODE_TYPES.ImportDefaultSpecifier; + local: Identifier; } diff --git a/packages/ast-spec/src/special/ImportNamespaceSpecifier/spec.ts b/packages/ast-spec/src/special/ImportNamespaceSpecifier/spec.ts index e4fd7eb3da01..eec79636f1fc 100644 --- a/packages/ast-spec/src/special/ImportNamespaceSpecifier/spec.ts +++ b/packages/ast-spec/src/special/ImportNamespaceSpecifier/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Identifier } from '../../expression/Identifier/spec'; export interface ImportNamespaceSpecifier extends BaseNode { - local: Identifier; type: AST_NODE_TYPES.ImportNamespaceSpecifier; + local: Identifier; } diff --git a/packages/ast-spec/src/special/ImportSpecifier/spec.ts b/packages/ast-spec/src/special/ImportSpecifier/spec.ts index 3589c88da438..5a47d0211678 100644 --- a/packages/ast-spec/src/special/ImportSpecifier/spec.ts +++ b/packages/ast-spec/src/special/ImportSpecifier/spec.ts @@ -4,8 +4,8 @@ import type { ImportKind } from '../../declaration/ExportAndImportKind'; import type { Identifier } from '../../expression/Identifier/spec'; export interface ImportSpecifier extends BaseNode { + type: AST_NODE_TYPES.ImportSpecifier; imported: Identifier; importKind: ImportKind; local: Identifier; - type: AST_NODE_TYPES.ImportSpecifier; } diff --git a/packages/ast-spec/src/special/PrivateIdentifier/spec.ts b/packages/ast-spec/src/special/PrivateIdentifier/spec.ts index 544366dd9c31..f1d2172b8374 100644 --- a/packages/ast-spec/src/special/PrivateIdentifier/spec.ts +++ b/packages/ast-spec/src/special/PrivateIdentifier/spec.ts @@ -2,6 +2,6 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; export interface PrivateIdentifier extends BaseNode { - name: string; type: AST_NODE_TYPES.PrivateIdentifier; + name: string; } diff --git a/packages/ast-spec/src/special/Program/spec.ts b/packages/ast-spec/src/special/Program/spec.ts index 3c7d153579d9..7bcee6cf8194 100644 --- a/packages/ast-spec/src/special/Program/spec.ts +++ b/packages/ast-spec/src/special/Program/spec.ts @@ -5,9 +5,9 @@ import type { ProgramStatement } from '../../unions/Statement'; import type { Token } from '../../unions/Token'; export interface Program extends NodeOrTokenData { + type: AST_NODE_TYPES.Program; body: ProgramStatement[]; comments: Comment[] | undefined; sourceType: 'module' | 'script'; tokens: Token[] | undefined; - type: AST_NODE_TYPES.Program; } diff --git a/packages/ast-spec/src/special/SwitchCase/spec.ts b/packages/ast-spec/src/special/SwitchCase/spec.ts index 3831472648ca..be9652680c03 100644 --- a/packages/ast-spec/src/special/SwitchCase/spec.ts +++ b/packages/ast-spec/src/special/SwitchCase/spec.ts @@ -4,7 +4,7 @@ import type { Expression } from '../../unions/Expression'; import type { Statement } from '../../unions/Statement'; export interface SwitchCase extends BaseNode { + type: AST_NODE_TYPES.SwitchCase; consequent: Statement[]; test: Expression | null; - type: AST_NODE_TYPES.SwitchCase; } diff --git a/packages/ast-spec/src/special/TSEnumBody/spec.ts b/packages/ast-spec/src/special/TSEnumBody/spec.ts index 82cf9cc700e7..3cdb9bbccdf0 100644 --- a/packages/ast-spec/src/special/TSEnumBody/spec.ts +++ b/packages/ast-spec/src/special/TSEnumBody/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TSEnumMember } from '../../element/TSEnumMember/spec'; export interface TSEnumBody extends BaseNode { - members: TSEnumMember[]; type: AST_NODE_TYPES.TSEnumBody; + members: TSEnumMember[]; } diff --git a/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts b/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts index ac5a347e53bd..debc2daff41b 100644 --- a/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts +++ b/packages/ast-spec/src/special/TSExternalModuleReference/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { StringLiteral } from '../../expression/literal/StringLiteral/spec'; export interface TSExternalModuleReference extends BaseNode { - expression: StringLiteral; type: AST_NODE_TYPES.TSExternalModuleReference; + expression: StringLiteral; } diff --git a/packages/ast-spec/src/special/TSInterfaceBody/spec.ts b/packages/ast-spec/src/special/TSInterfaceBody/spec.ts index 6cd50199ab3c..1ee1c901c14d 100644 --- a/packages/ast-spec/src/special/TSInterfaceBody/spec.ts +++ b/packages/ast-spec/src/special/TSInterfaceBody/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeElement } from '../../unions/TypeElement'; export interface TSInterfaceBody extends BaseNode { - body: TypeElement[]; type: AST_NODE_TYPES.TSInterfaceBody; + body: TypeElement[]; } diff --git a/packages/ast-spec/src/special/TSModuleBlock/spec.ts b/packages/ast-spec/src/special/TSModuleBlock/spec.ts index 14cf0b1a01aa..9fed19af3b80 100644 --- a/packages/ast-spec/src/special/TSModuleBlock/spec.ts +++ b/packages/ast-spec/src/special/TSModuleBlock/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { ProgramStatement } from '../../unions/Statement'; export interface TSModuleBlock extends BaseNode { - body: ProgramStatement[]; type: AST_NODE_TYPES.TSModuleBlock; + body: ProgramStatement[]; } diff --git a/packages/ast-spec/src/special/TSTypeParameter/spec.ts b/packages/ast-spec/src/special/TSTypeParameter/spec.ts index 1e4ecb4b659c..1b31eab7e2ec 100644 --- a/packages/ast-spec/src/special/TSTypeParameter/spec.ts +++ b/packages/ast-spec/src/special/TSTypeParameter/spec.ts @@ -4,11 +4,11 @@ import type { Identifier } from '../../expression/Identifier/spec'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSTypeParameter extends BaseNode { + type: AST_NODE_TYPES.TSTypeParameter; const: boolean; constraint: TypeNode | undefined; default: TypeNode | undefined; in: boolean; name: Identifier; out: boolean; - type: AST_NODE_TYPES.TSTypeParameter; } diff --git a/packages/ast-spec/src/special/TSTypeParameterDeclaration/spec.ts b/packages/ast-spec/src/special/TSTypeParameterDeclaration/spec.ts index 4e91a27bba7f..ac8971e38a0c 100644 --- a/packages/ast-spec/src/special/TSTypeParameterDeclaration/spec.ts +++ b/packages/ast-spec/src/special/TSTypeParameterDeclaration/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TSTypeParameter } from '../TSTypeParameter/spec'; export interface TSTypeParameterDeclaration extends BaseNode { - params: TSTypeParameter[]; type: AST_NODE_TYPES.TSTypeParameterDeclaration; + params: TSTypeParameter[]; } diff --git a/packages/ast-spec/src/special/TSTypeParameterInstantiation/spec.ts b/packages/ast-spec/src/special/TSTypeParameterInstantiation/spec.ts index 1f0df78b0a72..e5122c2f6a5c 100644 --- a/packages/ast-spec/src/special/TSTypeParameterInstantiation/spec.ts +++ b/packages/ast-spec/src/special/TSTypeParameterInstantiation/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSTypeParameterInstantiation extends BaseNode { - params: TypeNode[]; type: AST_NODE_TYPES.TSTypeParameterInstantiation; + params: TypeNode[]; } diff --git a/packages/ast-spec/src/special/TemplateElement/spec.ts b/packages/ast-spec/src/special/TemplateElement/spec.ts index c7e7833de575..cb5d1c6e76f8 100644 --- a/packages/ast-spec/src/special/TemplateElement/spec.ts +++ b/packages/ast-spec/src/special/TemplateElement/spec.ts @@ -2,8 +2,8 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; export interface TemplateElement extends BaseNode { - tail: boolean; type: AST_NODE_TYPES.TemplateElement; + tail: boolean; value: { cooked: string; raw: string; diff --git a/packages/ast-spec/src/special/VariableDeclarator/spec.ts b/packages/ast-spec/src/special/VariableDeclarator/spec.ts index c9cacd9c37e8..d6d9e985e28b 100644 --- a/packages/ast-spec/src/special/VariableDeclarator/spec.ts +++ b/packages/ast-spec/src/special/VariableDeclarator/spec.ts @@ -7,6 +7,7 @@ import type { Expression } from '../../unions/Expression'; // TODO: these declarator types can probably be refined further, especially // their differences when used in different contexts (e.g. for...of) interface VariableDeclaratorBase extends BaseNode { + type: AST_NODE_TYPES.VariableDeclarator; /** * Whether there's definite assignment assertion (`let x!: number`). * If `true`, then: `id` must be an identifier with a type annotation, @@ -22,7 +23,6 @@ interface VariableDeclaratorBase extends BaseNode { * in a `declare const`. */ init: Expression | null; - type: AST_NODE_TYPES.VariableDeclarator; } export interface VariableDeclaratorNoInit extends VariableDeclaratorBase { diff --git a/packages/ast-spec/src/statement/BlockStatement/spec.ts b/packages/ast-spec/src/statement/BlockStatement/spec.ts index 430ba2383202..298a962e5161 100644 --- a/packages/ast-spec/src/statement/BlockStatement/spec.ts +++ b/packages/ast-spec/src/statement/BlockStatement/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Statement } from '../../unions/Statement'; export interface BlockStatement extends BaseNode { - body: Statement[]; type: AST_NODE_TYPES.BlockStatement; + body: Statement[]; } diff --git a/packages/ast-spec/src/statement/BreakStatement/spec.ts b/packages/ast-spec/src/statement/BreakStatement/spec.ts index 3048106c5191..0441c298d365 100644 --- a/packages/ast-spec/src/statement/BreakStatement/spec.ts +++ b/packages/ast-spec/src/statement/BreakStatement/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Identifier } from '../../expression/Identifier/spec'; export interface BreakStatement extends BaseNode { - label: Identifier | null; type: AST_NODE_TYPES.BreakStatement; + label: Identifier | null; } diff --git a/packages/ast-spec/src/statement/ContinueStatement/spec.ts b/packages/ast-spec/src/statement/ContinueStatement/spec.ts index 82e7b80bda4a..70f2373dc217 100644 --- a/packages/ast-spec/src/statement/ContinueStatement/spec.ts +++ b/packages/ast-spec/src/statement/ContinueStatement/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Identifier } from '../../expression/Identifier/spec'; export interface ContinueStatement extends BaseNode { - label: Identifier | null; type: AST_NODE_TYPES.ContinueStatement; + label: Identifier | null; } diff --git a/packages/ast-spec/src/statement/DoWhileStatement/spec.ts b/packages/ast-spec/src/statement/DoWhileStatement/spec.ts index 04f00b7d2fb1..08d65edb667e 100644 --- a/packages/ast-spec/src/statement/DoWhileStatement/spec.ts +++ b/packages/ast-spec/src/statement/DoWhileStatement/spec.ts @@ -4,7 +4,7 @@ import type { Expression } from '../../unions/Expression'; import type { Statement } from '../../unions/Statement'; export interface DoWhileStatement extends BaseNode { + type: AST_NODE_TYPES.DoWhileStatement; body: Statement; test: Expression; - type: AST_NODE_TYPES.DoWhileStatement; } diff --git a/packages/ast-spec/src/statement/ExpressionStatement/spec.ts b/packages/ast-spec/src/statement/ExpressionStatement/spec.ts index 1f260232bfa8..fec0b820527a 100644 --- a/packages/ast-spec/src/statement/ExpressionStatement/spec.ts +++ b/packages/ast-spec/src/statement/ExpressionStatement/spec.ts @@ -3,7 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface ExpressionStatement extends BaseNode { + type: AST_NODE_TYPES.ExpressionStatement; directive: string | undefined; expression: Expression; - type: AST_NODE_TYPES.ExpressionStatement; } diff --git a/packages/ast-spec/src/statement/ForInStatement/spec.ts b/packages/ast-spec/src/statement/ForInStatement/spec.ts index e6176e4c4d38..9e4840d9e2f1 100644 --- a/packages/ast-spec/src/statement/ForInStatement/spec.ts +++ b/packages/ast-spec/src/statement/ForInStatement/spec.ts @@ -5,8 +5,8 @@ import type { ForInitialiser } from '../../unions/ForInitialiser'; import type { Statement } from '../../unions/Statement'; export interface ForInStatement extends BaseNode { + type: AST_NODE_TYPES.ForInStatement; body: Statement; left: ForInitialiser; right: Expression; - type: AST_NODE_TYPES.ForInStatement; } diff --git a/packages/ast-spec/src/statement/ForOfStatement/spec.ts b/packages/ast-spec/src/statement/ForOfStatement/spec.ts index 9fa6113b9e03..43e5860f8910 100644 --- a/packages/ast-spec/src/statement/ForOfStatement/spec.ts +++ b/packages/ast-spec/src/statement/ForOfStatement/spec.ts @@ -5,9 +5,9 @@ import type { ForOfInitialiser } from '../../unions/ForOfInitialiser'; import type { Statement } from '../../unions/Statement'; export interface ForOfStatement extends BaseNode { + type: AST_NODE_TYPES.ForOfStatement; await: boolean; body: Statement; left: ForOfInitialiser; right: Expression; - type: AST_NODE_TYPES.ForOfStatement; } diff --git a/packages/ast-spec/src/statement/ForStatement/spec.ts b/packages/ast-spec/src/statement/ForStatement/spec.ts index d9ff7df4c82f..9406b6a89484 100644 --- a/packages/ast-spec/src/statement/ForStatement/spec.ts +++ b/packages/ast-spec/src/statement/ForStatement/spec.ts @@ -5,9 +5,9 @@ import type { ForInitialiser } from '../../unions/ForInitialiser'; import type { Statement } from '../../unions/Statement'; export interface ForStatement extends BaseNode { + type: AST_NODE_TYPES.ForStatement; body: Statement; init: Expression | ForInitialiser | null; test: Expression | null; - type: AST_NODE_TYPES.ForStatement; update: Expression | null; } diff --git a/packages/ast-spec/src/statement/IfStatement/spec.ts b/packages/ast-spec/src/statement/IfStatement/spec.ts index 6159260797b8..9c2589bd661e 100644 --- a/packages/ast-spec/src/statement/IfStatement/spec.ts +++ b/packages/ast-spec/src/statement/IfStatement/spec.ts @@ -4,8 +4,8 @@ import type { Expression } from '../../unions/Expression'; import type { Statement } from '../../unions/Statement'; export interface IfStatement extends BaseNode { + type: AST_NODE_TYPES.IfStatement; alternate: Statement | null; consequent: Statement; test: Expression; - type: AST_NODE_TYPES.IfStatement; } diff --git a/packages/ast-spec/src/statement/LabeledStatement/spec.ts b/packages/ast-spec/src/statement/LabeledStatement/spec.ts index a8e0c9896215..d0dc83fd7117 100644 --- a/packages/ast-spec/src/statement/LabeledStatement/spec.ts +++ b/packages/ast-spec/src/statement/LabeledStatement/spec.ts @@ -4,7 +4,7 @@ import type { Identifier } from '../../expression/Identifier/spec'; import type { Statement } from '../../unions/Statement'; export interface LabeledStatement extends BaseNode { + type: AST_NODE_TYPES.LabeledStatement; body: Statement; label: Identifier; - type: AST_NODE_TYPES.LabeledStatement; } diff --git a/packages/ast-spec/src/statement/ReturnStatement/spec.ts b/packages/ast-spec/src/statement/ReturnStatement/spec.ts index 6fdaa911878a..d7758715c8dd 100644 --- a/packages/ast-spec/src/statement/ReturnStatement/spec.ts +++ b/packages/ast-spec/src/statement/ReturnStatement/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface ReturnStatement extends BaseNode { - argument: Expression | null; type: AST_NODE_TYPES.ReturnStatement; + argument: Expression | null; } diff --git a/packages/ast-spec/src/statement/SwitchStatement/spec.ts b/packages/ast-spec/src/statement/SwitchStatement/spec.ts index 63152d55bdf5..f7785b3f2527 100644 --- a/packages/ast-spec/src/statement/SwitchStatement/spec.ts +++ b/packages/ast-spec/src/statement/SwitchStatement/spec.ts @@ -4,7 +4,7 @@ import type { SwitchCase } from '../../special/SwitchCase/spec'; import type { Expression } from '../../unions/Expression'; export interface SwitchStatement extends BaseNode { + type: AST_NODE_TYPES.SwitchStatement; cases: SwitchCase[]; discriminant: Expression; - type: AST_NODE_TYPES.SwitchStatement; } diff --git a/packages/ast-spec/src/statement/TSExportAssignment/spec.ts b/packages/ast-spec/src/statement/TSExportAssignment/spec.ts index 7096aee66cde..3792bc5012b1 100644 --- a/packages/ast-spec/src/statement/TSExportAssignment/spec.ts +++ b/packages/ast-spec/src/statement/TSExportAssignment/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface TSExportAssignment extends BaseNode { - expression: Expression; type: AST_NODE_TYPES.TSExportAssignment; + expression: Expression; } diff --git a/packages/ast-spec/src/statement/ThrowStatement/spec.ts b/packages/ast-spec/src/statement/ThrowStatement/spec.ts index 5372a269e653..618b655c2390 100644 --- a/packages/ast-spec/src/statement/ThrowStatement/spec.ts +++ b/packages/ast-spec/src/statement/ThrowStatement/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Expression } from '../../unions/Expression'; export interface ThrowStatement extends BaseNode { - argument: Expression; type: AST_NODE_TYPES.ThrowStatement; + argument: Expression; } diff --git a/packages/ast-spec/src/statement/TryStatement/spec.ts b/packages/ast-spec/src/statement/TryStatement/spec.ts index b786996b3064..7dbe0c1b9dc1 100644 --- a/packages/ast-spec/src/statement/TryStatement/spec.ts +++ b/packages/ast-spec/src/statement/TryStatement/spec.ts @@ -4,8 +4,8 @@ import type { CatchClause } from '../../special/CatchClause/spec'; import type { BlockStatement } from '../BlockStatement/spec'; export interface TryStatement extends BaseNode { + type: AST_NODE_TYPES.TryStatement; block: BlockStatement; finalizer: BlockStatement | null; handler: CatchClause | null; - type: AST_NODE_TYPES.TryStatement; } diff --git a/packages/ast-spec/src/statement/WhileStatement/spec.ts b/packages/ast-spec/src/statement/WhileStatement/spec.ts index e4ef33c229ae..5656d10b4c04 100644 --- a/packages/ast-spec/src/statement/WhileStatement/spec.ts +++ b/packages/ast-spec/src/statement/WhileStatement/spec.ts @@ -4,7 +4,7 @@ import type { Expression } from '../../unions/Expression'; import type { Statement } from '../../unions/Statement'; export interface WhileStatement extends BaseNode { + type: AST_NODE_TYPES.WhileStatement; body: Statement; test: Expression; - type: AST_NODE_TYPES.WhileStatement; } diff --git a/packages/ast-spec/src/statement/WithStatement/spec.ts b/packages/ast-spec/src/statement/WithStatement/spec.ts index 71b9b3936765..123bfeb4f04a 100644 --- a/packages/ast-spec/src/statement/WithStatement/spec.ts +++ b/packages/ast-spec/src/statement/WithStatement/spec.ts @@ -4,7 +4,7 @@ import type { Expression } from '../../unions/Expression'; import type { Statement } from '../../unions/Statement'; export interface WithStatement extends BaseNode { + type: AST_NODE_TYPES.WithStatement; body: Statement; object: Expression; - type: AST_NODE_TYPES.WithStatement; } diff --git a/packages/ast-spec/src/token/RegularExpressionToken/spec.ts b/packages/ast-spec/src/token/RegularExpressionToken/spec.ts index a60a58d1ecbd..e13adc73d914 100644 --- a/packages/ast-spec/src/token/RegularExpressionToken/spec.ts +++ b/packages/ast-spec/src/token/RegularExpressionToken/spec.ts @@ -2,9 +2,9 @@ import type { AST_TOKEN_TYPES } from '../../ast-token-types'; import type { BaseToken } from '../../base/BaseToken'; export interface RegularExpressionToken extends BaseToken { + type: AST_TOKEN_TYPES.RegularExpression; regex: { flags: string; pattern: string; }; - type: AST_TOKEN_TYPES.RegularExpression; } diff --git a/packages/ast-spec/src/type/TSArrayType/spec.ts b/packages/ast-spec/src/type/TSArrayType/spec.ts index 82a71db74c37..f7aa4f16a596 100644 --- a/packages/ast-spec/src/type/TSArrayType/spec.ts +++ b/packages/ast-spec/src/type/TSArrayType/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSArrayType extends BaseNode { - elementType: TypeNode; type: AST_NODE_TYPES.TSArrayType; + elementType: TypeNode; } diff --git a/packages/ast-spec/src/type/TSConditionalType/spec.ts b/packages/ast-spec/src/type/TSConditionalType/spec.ts index 59ec3ece270b..50a1e96387dd 100644 --- a/packages/ast-spec/src/type/TSConditionalType/spec.ts +++ b/packages/ast-spec/src/type/TSConditionalType/spec.ts @@ -3,9 +3,9 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSConditionalType extends BaseNode { + type: AST_NODE_TYPES.TSConditionalType; checkType: TypeNode; extendsType: TypeNode; falseType: TypeNode; trueType: TypeNode; - type: AST_NODE_TYPES.TSConditionalType; } diff --git a/packages/ast-spec/src/type/TSConstructorType/spec.ts b/packages/ast-spec/src/type/TSConstructorType/spec.ts index b6d5797f39c6..08e19757d14a 100644 --- a/packages/ast-spec/src/type/TSConstructorType/spec.ts +++ b/packages/ast-spec/src/type/TSConstructorType/spec.ts @@ -2,6 +2,6 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { TSFunctionSignatureBase } from '../../base/TSFunctionSignatureBase'; export interface TSConstructorType extends TSFunctionSignatureBase { - abstract: boolean; type: AST_NODE_TYPES.TSConstructorType; + abstract: boolean; } diff --git a/packages/ast-spec/src/type/TSImportType/spec.ts b/packages/ast-spec/src/type/TSImportType/spec.ts index 34143ed919c4..f85074de7154 100644 --- a/packages/ast-spec/src/type/TSImportType/spec.ts +++ b/packages/ast-spec/src/type/TSImportType/spec.ts @@ -5,8 +5,8 @@ import type { EntityName } from '../../unions/EntityName'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSImportType extends BaseNode { + type: AST_NODE_TYPES.TSImportType; argument: TypeNode; qualifier: EntityName | null; - type: AST_NODE_TYPES.TSImportType; typeArguments: TSTypeParameterInstantiation | null; } diff --git a/packages/ast-spec/src/type/TSIndexedAccessType/spec.ts b/packages/ast-spec/src/type/TSIndexedAccessType/spec.ts index 54b1fd99b074..57dbc58c4cac 100644 --- a/packages/ast-spec/src/type/TSIndexedAccessType/spec.ts +++ b/packages/ast-spec/src/type/TSIndexedAccessType/spec.ts @@ -3,7 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSIndexedAccessType extends BaseNode { + type: AST_NODE_TYPES.TSIndexedAccessType; indexType: TypeNode; objectType: TypeNode; - type: AST_NODE_TYPES.TSIndexedAccessType; } diff --git a/packages/ast-spec/src/type/TSLiteralType/spec.ts b/packages/ast-spec/src/type/TSLiteralType/spec.ts index ccdd0265ac2b..39f6ae0d2961 100644 --- a/packages/ast-spec/src/type/TSLiteralType/spec.ts +++ b/packages/ast-spec/src/type/TSLiteralType/spec.ts @@ -5,6 +5,6 @@ import type { UpdateExpression } from '../../expression/UpdateExpression/spec'; import type { LiteralExpression } from '../../unions/LiteralExpression'; export interface TSLiteralType extends BaseNode { - literal: LiteralExpression | UnaryExpression | UpdateExpression; type: AST_NODE_TYPES.TSLiteralType; + literal: LiteralExpression | UnaryExpression | UpdateExpression; } diff --git a/packages/ast-spec/src/type/TSMappedType/spec.ts b/packages/ast-spec/src/type/TSMappedType/spec.ts index 46041e852dcc..c78d4ce5fe97 100644 --- a/packages/ast-spec/src/type/TSMappedType/spec.ts +++ b/packages/ast-spec/src/type/TSMappedType/spec.ts @@ -5,12 +5,12 @@ import type { TSTypeParameter } from '../../special/TSTypeParameter/spec'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSMappedType extends BaseNode { + type: AST_NODE_TYPES.TSMappedType; constraint: TypeNode; key: Identifier; nameType: TypeNode | null; optional: '+' | '-' | boolean | undefined; readonly: '+' | '-' | boolean | undefined; - type: AST_NODE_TYPES.TSMappedType; typeAnnotation: TypeNode | undefined; /** @deprecated Use {@link `constraint`} and {@link `key`} instead. */ typeParameter: TSTypeParameter; diff --git a/packages/ast-spec/src/type/TSNamedTupleMember/spec.ts b/packages/ast-spec/src/type/TSNamedTupleMember/spec.ts index 29e5fadf4d3d..540d8bf19db5 100644 --- a/packages/ast-spec/src/type/TSNamedTupleMember/spec.ts +++ b/packages/ast-spec/src/type/TSNamedTupleMember/spec.ts @@ -4,8 +4,8 @@ import type { Identifier } from '../../expression/Identifier/spec'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSNamedTupleMember extends BaseNode { + type: AST_NODE_TYPES.TSNamedTupleMember; elementType: TypeNode; label: Identifier; optional: boolean; - type: AST_NODE_TYPES.TSNamedTupleMember; } diff --git a/packages/ast-spec/src/type/TSQualifiedName/spec.ts b/packages/ast-spec/src/type/TSQualifiedName/spec.ts index 61c61702b5b9..cdd6feeee0ef 100644 --- a/packages/ast-spec/src/type/TSQualifiedName/spec.ts +++ b/packages/ast-spec/src/type/TSQualifiedName/spec.ts @@ -4,7 +4,7 @@ import type { Identifier } from '../../expression/Identifier/spec'; import type { EntityName } from '../../unions/EntityName'; export interface TSQualifiedName extends BaseNode { + type: AST_NODE_TYPES.TSQualifiedName; left: EntityName; right: Identifier; - type: AST_NODE_TYPES.TSQualifiedName; } diff --git a/packages/ast-spec/src/type/TSTemplateLiteralType/spec.ts b/packages/ast-spec/src/type/TSTemplateLiteralType/spec.ts index 052b82c6241b..c2e8783da873 100644 --- a/packages/ast-spec/src/type/TSTemplateLiteralType/spec.ts +++ b/packages/ast-spec/src/type/TSTemplateLiteralType/spec.ts @@ -4,7 +4,7 @@ import type { TemplateElement } from '../../special/TemplateElement/spec'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSTemplateLiteralType extends BaseNode { - quasis: TemplateElement[]; type: AST_NODE_TYPES.TSTemplateLiteralType; + quasis: TemplateElement[]; types: TypeNode[]; } diff --git a/packages/ast-spec/src/type/TSTupleType/spec.ts b/packages/ast-spec/src/type/TSTupleType/spec.ts index 58fec673558b..641a0c15b4c6 100644 --- a/packages/ast-spec/src/type/TSTupleType/spec.ts +++ b/packages/ast-spec/src/type/TSTupleType/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSTupleType extends BaseNode { - elementTypes: TypeNode[]; type: AST_NODE_TYPES.TSTupleType; + elementTypes: TypeNode[]; } diff --git a/packages/ast-spec/src/type/TSTypeLiteral/spec.ts b/packages/ast-spec/src/type/TSTypeLiteral/spec.ts index 838a7e7e97a0..243179d23d9f 100644 --- a/packages/ast-spec/src/type/TSTypeLiteral/spec.ts +++ b/packages/ast-spec/src/type/TSTypeLiteral/spec.ts @@ -3,6 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeElement } from '../../unions/TypeElement'; export interface TSTypeLiteral extends BaseNode { - members: TypeElement[]; type: AST_NODE_TYPES.TSTypeLiteral; + members: TypeElement[]; } diff --git a/packages/ast-spec/src/type/TSTypeOperator/spec.ts b/packages/ast-spec/src/type/TSTypeOperator/spec.ts index f38c0fc950cb..f6d530c084f0 100644 --- a/packages/ast-spec/src/type/TSTypeOperator/spec.ts +++ b/packages/ast-spec/src/type/TSTypeOperator/spec.ts @@ -3,7 +3,7 @@ import type { BaseNode } from '../../base/BaseNode'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSTypeOperator extends BaseNode { - operator: 'keyof' | 'readonly' | 'unique'; type: AST_NODE_TYPES.TSTypeOperator; + operator: 'keyof' | 'readonly' | 'unique'; typeAnnotation: TypeNode | undefined; } diff --git a/packages/ast-spec/src/type/TSTypePredicate/spec.ts b/packages/ast-spec/src/type/TSTypePredicate/spec.ts index dd296ebc2a24..cd34a31bcaf0 100644 --- a/packages/ast-spec/src/type/TSTypePredicate/spec.ts +++ b/packages/ast-spec/src/type/TSTypePredicate/spec.ts @@ -5,8 +5,8 @@ import type { TSTypeAnnotation } from '../../special/TSTypeAnnotation/spec'; import type { TSThisType } from '../TSThisType/spec'; export interface TSTypePredicate extends BaseNode { + type: AST_NODE_TYPES.TSTypePredicate; asserts: boolean; parameterName: Identifier | TSThisType; - type: AST_NODE_TYPES.TSTypePredicate; typeAnnotation: TSTypeAnnotation | null; } diff --git a/packages/ast-spec/src/type/TSTypeQuery/spec.ts b/packages/ast-spec/src/type/TSTypeQuery/spec.ts index 0a09d2cf48a5..e5cad59b9c71 100644 --- a/packages/ast-spec/src/type/TSTypeQuery/spec.ts +++ b/packages/ast-spec/src/type/TSTypeQuery/spec.ts @@ -5,7 +5,7 @@ import type { EntityName } from '../../unions/EntityName'; import type { TSImportType } from '../TSImportType/spec'; export interface TSTypeQuery extends BaseNode { - exprName: EntityName | TSImportType; type: AST_NODE_TYPES.TSTypeQuery; + exprName: EntityName | TSImportType; typeArguments: TSTypeParameterInstantiation | undefined; } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 062f196a7111..2ec32622a71f 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,3 +1,41 @@ +## 8.9.0 (2024-10-14) + + +### 🩹 Fixes + +- **eslint-plugin:** [no-unnecessary-type-parameters] cannot assume variables are either type or value + +- **scope-manager:** [no-use-before-define] do not treat nested namespace aliases as variable references + +- **eslint-plugin:** [return-await] sync the behavior with await-thenable + +- **eslint-plugin:** [prefer-literal-enum-member] report a different error message when `allowBitwiseExpressions` is enabled + +- **eslint-plugin:** [no-loop-func] sync from upstream base rule + +- **eslint-plugin:** [no-unused-vars] never report the naming of an enum member + +- **eslint-plugin:** correct use-at-your-own-risk type definitions + +- **eslint-plugin:** handle unions in await...for + + +### ❤️ Thank You + +- Abraham Guo +- Anna Bocharova +- Arya Emami +- auvred +- Joshua Chen +- Kirk Waiblinger +- Lotfi Meklati +- mdm317 +- Ronen Amiel +- Sukka +- YeonJuan + +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. + ## 8.8.1 (2024-10-07) diff --git a/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md b/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md index eab5e19d3586..32216cb0f480 100644 --- a/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md +++ b/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md @@ -184,7 +184,7 @@ It lists all TSLint rules along side rules from the ESLint ecosystem that are th | [`object-literal-shorthand`] | 🌟 | [`object-shorthand`][object-shorthand] | | [`one-line`] | 🌟 | [`brace-style`][brace-style] or [Prettier] | | [`one-variable-per-declaration`] | 🌟 | [`one-var`][one-var] | -| [`ordered-imports`] | 🌓 | [`import/order`] | +| [`ordered-imports`] | 🔌 | [`import/order`] | | [`prefer-function-over-method`] | 🌟 | [`@typescript-eslint/class-methods-use-this`] | | [`prefer-method-signature`] | ✅ | [`@typescript-eslint/method-signature-style`] | | [`prefer-switch`] | 🛑 | N/A | diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-call.mdx b/packages/eslint-plugin/docs/rules/no-unsafe-call.mdx index 6fc46d6cbf18..540d464de8ed 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-call.mdx +++ b/packages/eslint-plugin/docs/rules/no-unsafe-call.mdx @@ -78,7 +78,7 @@ Note that whereas [no-unsafe-function-type](./no-unsafe-function-type.mdx) helps See, for example, the following code: ```ts -function unsafe(maybeFunction: unknown): string { +function callUnsafe(maybeFunction: unknown): string { if (typeof maybeFunction === 'function') { // TypeScript allows this, but it's completely unsound. return maybeFunction('call', 'with', 'any', 'args'); @@ -87,6 +87,23 @@ function unsafe(maybeFunction: unknown): string { } ``` +In this sort of situation, beware that there is no way to guarantee with runtime checks that a value is safe to call. +If you _really_ want to call a value whose type you don't know, your best best is to use a `try`/`catch` and suppress any TypeScript or linter errors that get in your way. + +```ts +function callSafe(maybeFunction: unknown): void { + try { + // intentionally unsound type assertion + (maybeFunction as () => unknown)(); + } catch (e) { + console.error( + 'Function either could not be called or threw an error when called: ', + e, + ); + } +} +``` + ## When Not To Use It If your codebase has many existing `any`s or areas of unsafe code, it may be difficult to enable this rule. diff --git a/packages/eslint-plugin/eslint-recommended-raw.d.ts b/packages/eslint-plugin/eslint-recommended-raw.d.ts index da4a8496b4f3..43d6a6c9326e 100644 --- a/packages/eslint-plugin/eslint-recommended-raw.d.ts +++ b/packages/eslint-plugin/eslint-recommended-raw.d.ts @@ -2,4 +2,4 @@ declare const config: (style: 'glob' | 'minimatch') => { files: string[]; rules: Record; }; -export default config; +export = config; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 5d56497ab27c..3272ee70e646 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,10 +1,11 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "8.8.1", + "version": "8.9.0", "description": "TypeScript plugin for ESLint", "files": [ "dist", "docs", + "eslint-recommended-raw.d.ts", "index.d.ts", "rules.d.ts", "package.json", @@ -60,10 +61,10 @@ }, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.9.0", + "@typescript-eslint/type-utils": "8.9.0", + "@typescript-eslint/utils": "8.9.0", + "@typescript-eslint/visitor-keys": "8.9.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -74,8 +75,8 @@ "@types/marked": "^5.0.2", "@types/mdast": "^4.0.3", "@types/natural-compare": "*", - "@typescript-eslint/rule-schema-to-typescript-types": "8.8.1", - "@typescript-eslint/rule-tester": "8.8.1", + "@typescript-eslint/rule-schema-to-typescript-types": "8.9.0", + "@typescript-eslint/rule-tester": "8.9.0", "ajv": "^6.12.6", "cross-env": "^7.0.3", "cross-fetch": "*", diff --git a/packages/eslint-plugin/rules.d.ts b/packages/eslint-plugin/rules.d.ts index d6625d09950e..7808db85f056 100644 --- a/packages/eslint-plugin/rules.d.ts +++ b/packages/eslint-plugin/rules.d.ts @@ -41,7 +41,7 @@ import type { RuleRecommendationAcrossConfigs, } from '@typescript-eslint/utils/ts-eslint'; -export interface ESLintPluginDocs { +interface ESLintPluginDocs { /** * Does the rule extend (or is it based off of) an ESLint code rule? * Alternately accepts the name of the base rule, in case the rule has been renamed. @@ -62,16 +62,25 @@ export interface ESLintPluginDocs { requiresTypeChecking?: boolean; } -export type ESLintPluginRuleModule = RuleModuleWithMetaDocs< +type ESLintPluginRuleModule = RuleModuleWithMetaDocs< string, readonly unknown[], ESLintPluginDocs >; -export type TypeScriptESLintRules = Record< +type TypeScriptESLintRules = Record< string, RuleModuleWithMetaDocs >; declare const rules: TypeScriptESLintRules; -export default rules; + +declare namespace rules { + export type { + ESLintPluginDocs, + ESLintPluginRuleModule, + TypeScriptESLintRules, + }; +} + +export = rules; diff --git a/packages/eslint-plugin/src/configs/eslint-recommended-raw.ts b/packages/eslint-plugin/src/configs/eslint-recommended-raw.ts index d42cfb1d8663..e69033be2723 100644 --- a/packages/eslint-plugin/src/configs/eslint-recommended-raw.ts +++ b/packages/eslint-plugin/src/configs/eslint-recommended-raw.ts @@ -7,7 +7,7 @@ * - disables rules from eslint:recommended which are already handled by TypeScript. * - enables rules that make sense due to TS's typechecking / transpilation. */ -export default ( +const config = ( style: 'glob' | 'minimatch', ): { files: string[]; @@ -44,3 +44,5 @@ export default ( 'prefer-spread': 'error', // ts transpiles spread to apply, so no need for manual apply }, }); + +export = config; diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index a8d1265a9458..26303a172326 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -21,27 +21,31 @@ const { name, version } = require('../package.json') as { version: string; }; +const configs = { + all, + base, + 'disable-type-checked': disableTypeChecked, + 'eslint-recommended': eslintRecommended, + recommended, + /** @deprecated - please use "recommended-type-checked" instead. */ + 'recommended-requiring-type-checking': recommendedTypeChecked, + 'recommended-type-checked': recommendedTypeChecked, + 'recommended-type-checked-only': recommendedTypeCheckedOnly, + strict, + 'strict-type-checked': strictTypeChecked, + 'strict-type-checked-only': strictTypeCheckedOnly, + stylistic, + 'stylistic-type-checked': stylisticTypeChecked, + 'stylistic-type-checked-only': stylisticTypeCheckedOnly, +}; + +const meta = { + name, + version, +}; + export = { - configs: { - all, - base, - 'disable-type-checked': disableTypeChecked, - 'eslint-recommended': eslintRecommended, - recommended, - /** @deprecated - please use "recommended-type-checked" instead. */ - 'recommended-requiring-type-checking': recommendedTypeChecked, - 'recommended-type-checked': recommendedTypeChecked, - 'recommended-type-checked-only': recommendedTypeCheckedOnly, - strict, - 'strict-type-checked': strictTypeChecked, - 'strict-type-checked-only': strictTypeCheckedOnly, - stylistic, - 'stylistic-type-checked': stylisticTypeChecked, - 'stylistic-type-checked-only': stylisticTypeCheckedOnly, - }, - meta: { - name, - version, - }, + configs, + meta, rules, } satisfies Linter.Plugin; diff --git a/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts b/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts index 3661cc699e07..4729da05be7c 100644 --- a/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts +++ b/packages/eslint-plugin/src/rules/adjacent-overload-signatures.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, getNameFromMember, MemberNameType } from '../util'; @@ -28,17 +29,17 @@ export default createRule({ description: 'Require that function overload signatures be consecutive', recommended: 'stylistic', }, - schema: [], messages: { adjacentSignature: 'All {{name}} signatures should be adjacent.', }, + schema: [], }, defaultOptions: [], create(context) { interface Method { + callSignature: boolean; name: string; static?: boolean; - callSignature: boolean; type: MemberNameType; } @@ -69,28 +70,28 @@ export default createRule({ } return { name, - callSignature: false, type: MemberNameType.Normal, + callSignature: false, }; } case AST_NODE_TYPES.TSMethodSignature: case AST_NODE_TYPES.MethodDefinition: return { ...getNameFromMember(member, context.sourceCode), - static: !!member.static, callSignature: false, + static: !!member.static, }; case AST_NODE_TYPES.TSCallSignatureDeclaration: return { name: 'call', - callSignature: true, type: MemberNameType.Normal, + callSignature: true, }; case AST_NODE_TYPES.TSConstructSignatureDeclaration: return { name: 'new', - callSignature: false, type: MemberNameType.Normal, + callSignature: false, }; } @@ -154,12 +155,12 @@ export default createRule({ } return { + BlockStatement: checkBodyForOverloadMethods, ClassBody: checkBodyForOverloadMethods, Program: checkBodyForOverloadMethods, + TSInterfaceBody: checkBodyForOverloadMethods, TSModuleBlock: checkBodyForOverloadMethods, TSTypeLiteral: checkBodyForOverloadMethods, - TSInterfaceBody: checkBodyForOverloadMethods, - BlockStatement: checkBodyForOverloadMethods, }; }, }); diff --git a/packages/eslint-plugin/src/rules/array-type.ts b/packages/eslint-plugin/src/rules/array-type.ts index 742507ada730..08330497cb82 100644 --- a/packages/eslint-plugin/src/rules/array-type.ts +++ b/packages/eslint-plugin/src/rules/array-type.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isParenthesized } from '../util'; @@ -71,7 +72,7 @@ function typeNeedsParentheses(node: TSESTree.Node): boolean { } } -export type OptionString = 'array-simple' | 'array' | 'generic'; +export type OptionString = 'array' | 'array-simple' | 'generic'; type Options = [ { default: OptionString; @@ -80,11 +81,11 @@ type Options = [ ]; type MessageIds = | 'errorStringArray' + | 'errorStringArrayReadonly' | 'errorStringArraySimple' + | 'errorStringArraySimpleReadonly' | 'errorStringGeneric' - | 'errorStringGenericSimple' - | 'errorStringArrayReadonly' - | 'errorStringArraySimpleReadonly'; + | 'errorStringGenericSimple'; export default createRule({ name: 'array-type', @@ -97,21 +98,22 @@ export default createRule({ }, fixable: 'code', messages: { - errorStringGeneric: - "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden. Use '{{className}}<{{type}}>' instead.", errorStringArray: "Array type using '{{className}}<{{type}}>' is forbidden. Use '{{readonlyPrefix}}{{type}}[]' instead.", errorStringArrayReadonly: "Array type using '{{className}}<{{type}}>' is forbidden. Use '{{readonlyPrefix}}{{type}}' instead.", errorStringArraySimple: "Array type using '{{className}}<{{type}}>' is forbidden for simple types. Use '{{readonlyPrefix}}{{type}}[]' instead.", - errorStringGenericSimple: - "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden for non-simple types. Use '{{className}}<{{type}}>' instead.", errorStringArraySimpleReadonly: "Array type using '{{className}}<{{type}}>' is forbidden for simple types. Use '{{readonlyPrefix}}{{type}}' instead.", + errorStringGeneric: + "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden. Use '{{className}}<{{type}}>' instead.", + errorStringGenericSimple: + "Array type using '{{readonlyPrefix}}{{type}}[]' is forbidden for non-simple types. Use '{{className}}<{{type}}>' instead.", }, schema: [ { + type: 'object', $defs: { arrayOption: { type: 'string', @@ -130,7 +132,6 @@ export default createRule({ 'The array type expected for readonly cases. If omitted, the value for `default` will be used.', }, }, - type: 'object', }, ], }, @@ -178,9 +179,9 @@ export default createRule({ node: errorNode, messageId, data: { + type: getMessageType(node.elementType), className: isReadonly ? 'ReadonlyArray' : 'Array', readonlyPrefix: isReadonly ? 'readonly ' : '', - type: getMessageType(node.elementType), }, fix(fixer) { const typeNode = node.elementType; @@ -246,9 +247,9 @@ export default createRule({ node, messageId, data: { + type: 'any', className: isReadonlyArrayType ? 'ReadonlyArray' : 'Array', readonlyPrefix, - type: 'any', }, fix(fixer) { return fixer.replaceText(node, `${readonlyPrefix}any[]`); @@ -280,9 +281,9 @@ export default createRule({ node, messageId, data: { + type: getMessageType(type), className: isReadonlyArrayType ? node.typeName.name : 'Array', readonlyPrefix, - type: getMessageType(type), }, fix(fixer) { return [ diff --git a/packages/eslint-plugin/src/rules/await-thenable.ts b/packages/eslint-plugin/src/rules/await-thenable.ts index 4c5a2e6dd171..d71efd65139d 100644 --- a/packages/eslint-plugin/src/rules/await-thenable.ts +++ b/packages/eslint-plugin/src/rules/await-thenable.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import * as tsutils from 'ts-api-utils'; import { @@ -14,13 +15,14 @@ import { getForStatementHeadLoc } from '../util/getForStatementHeadLoc'; type MessageId = | 'await' + | 'convertToOrdinaryFor' | 'forAwaitOfNonThenable' - | 'removeAwait' - | 'convertToOrdinaryFor'; + | 'removeAwait'; export default createRule<[], MessageId>({ name: 'await-thenable', meta: { + type: 'problem', docs: { description: 'Disallow awaiting a value that is not a Thenable', recommended: 'recommended', @@ -29,13 +31,12 @@ export default createRule<[], MessageId>({ hasSuggestions: true, messages: { await: 'Unexpected `await` of a non-Promise (non-"Thenable") value.', + convertToOrdinaryFor: 'Convert to an ordinary `for...of` loop.', forAwaitOfNonThenable: 'Unexpected `for await...of` of a value that is not async iterable.', removeAwait: 'Remove unnecessary `await`.', - convertToOrdinaryFor: 'Convert to an ordinary `for...of` loop.', }, schema: [], - type: 'problem', }, defaultOptions: [], @@ -54,8 +55,8 @@ export default createRule<[], MessageId>({ if (!tsutils.isThenableType(checker, originalNode.expression, type)) { context.report({ - messageId: 'await', node, + messageId: 'await', suggest: [ { messageId: 'removeAwait', @@ -79,11 +80,16 @@ export default createRule<[], MessageId>({ return; } - const asyncIteratorSymbol = tsutils.getWellKnownSymbolPropertyOfType( - type, - 'asyncIterator', - checker, - ); + const asyncIteratorSymbol = tsutils + .unionTypeParts(type) + .map(t => + tsutils.getWellKnownSymbolPropertyOfType( + t, + 'asyncIterator', + checker, + ), + ) + .find(symbol => symbol != null); if (asyncIteratorSymbol == null) { context.report({ diff --git a/packages/eslint-plugin/src/rules/ban-ts-comment.ts b/packages/eslint-plugin/src/rules/ban-ts-comment.ts index 19001dcbd97a..3f1379b807ff 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-comment.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-comment.ts @@ -1,33 +1,34 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { createRule, getStringLength, nullThrows } from '../util'; type DirectiveConfig = - | boolean | 'allow-with-description' - | { descriptionFormat: string }; + | { descriptionFormat: string } + | boolean; interface Options { + minimumDescriptionLength?: number; + 'ts-check'?: DirectiveConfig; 'ts-expect-error'?: DirectiveConfig; 'ts-ignore'?: DirectiveConfig; 'ts-nocheck'?: DirectiveConfig; - 'ts-check'?: DirectiveConfig; - minimumDescriptionLength?: number; } const defaultMinimumDescriptionLength = 3; type MessageIds = + | 'replaceTsIgnoreWithTsExpectError' | 'tsDirectiveComment' - | 'tsIgnoreInsteadOfExpectError' | 'tsDirectiveCommentDescriptionNotMatchPattern' | 'tsDirectiveCommentRequiresDescription' - | 'replaceTsIgnoreWithTsExpectError'; + | 'tsIgnoreInsteadOfExpectError'; interface MatchedTSDirective { - directive: string; description: string; + directive: string; } export default createRule<[Options], MessageIds>({ @@ -42,21 +43,22 @@ export default createRule<[Options], MessageIds>({ strict: [{ minimumDescriptionLength: 10 }], }, }, + hasSuggestions: true, messages: { + replaceTsIgnoreWithTsExpectError: + 'Replace "@ts-ignore" with "@ts-expect-error".', tsDirectiveComment: 'Do not use "@ts-{{directive}}" because it alters compilation errors.', - tsIgnoreInsteadOfExpectError: - 'Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.', - tsDirectiveCommentRequiresDescription: - 'Include a description after the "@ts-{{directive}}" directive to explain why the @ts-{{directive}} is necessary. The description must be {{minimumDescriptionLength}} characters or longer.', tsDirectiveCommentDescriptionNotMatchPattern: 'The description for the "@ts-{{directive}}" directive must match the {{format}} format.', - replaceTsIgnoreWithTsExpectError: - 'Replace "@ts-ignore" with "@ts-expect-error".', + tsDirectiveCommentRequiresDescription: + 'Include a description after the "@ts-{{directive}}" directive to explain why the @ts-{{directive}} is necessary. The description must be {{minimumDescriptionLength}} characters or longer.', + tsIgnoreInsteadOfExpectError: + 'Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.', }, - hasSuggestions: true, schema: [ { + type: 'object', $defs: { directiveConfigSchema: { oneOf: [ @@ -78,30 +80,29 @@ export default createRule<[Options], MessageIds>({ ], }, }, + additionalProperties: false, properties: { - 'ts-expect-error': { $ref: '#/items/0/$defs/directiveConfigSchema' }, - 'ts-ignore': { $ref: '#/items/0/$defs/directiveConfigSchema' }, - 'ts-nocheck': { $ref: '#/items/0/$defs/directiveConfigSchema' }, - 'ts-check': { $ref: '#/items/0/$defs/directiveConfigSchema' }, minimumDescriptionLength: { - description: - 'A minimum character length for descriptions when `allow-with-description` is enabled.', type: 'number', default: defaultMinimumDescriptionLength, + description: + 'A minimum character length for descriptions when `allow-with-description` is enabled.', }, + 'ts-check': { $ref: '#/items/0/$defs/directiveConfigSchema' }, + 'ts-expect-error': { $ref: '#/items/0/$defs/directiveConfigSchema' }, + 'ts-ignore': { $ref: '#/items/0/$defs/directiveConfigSchema' }, + 'ts-nocheck': { $ref: '#/items/0/$defs/directiveConfigSchema' }, }, - type: 'object', - additionalProperties: false, }, ], }, defaultOptions: [ { + minimumDescriptionLength: defaultMinimumDescriptionLength, + 'ts-check': false, 'ts-expect-error': 'allow-with-description', 'ts-ignore': true, 'ts-nocheck': true, - 'ts-check': false, - minimumDescriptionLength: defaultMinimumDescriptionLength, }, ], create(context, [options]) { @@ -140,19 +141,19 @@ export default createRule<[Options], MessageIds>({ return null; } - const { directive, description } = nullThrows( + const { description, directive } = nullThrows( match.groups, 'RegExp should contain groups', ); return { - directive: nullThrows( - directive, - 'RegExp should contain "directive" group', - ), description: nullThrows( description, 'RegExp should contain "description" group', ), + directive: nullThrows( + directive, + 'RegExp should contain "directive" group', + ), }; } @@ -192,7 +193,7 @@ export default createRule<[Options], MessageIds>({ if (!match) { return; } - const { directive, description } = match; + const { description, directive } = match; if ( directive === 'nocheck' && @@ -231,9 +232,9 @@ export default createRule<[Options], MessageIds>({ }); } else { context.report({ - data: { directive }, node: comment, messageId: 'tsDirectiveComment', + data: { directive }, }); } } @@ -252,15 +253,15 @@ export default createRule<[Options], MessageIds>({ ) ) { context.report({ - data: { directive, minimumDescriptionLength }, node: comment, messageId: 'tsDirectiveCommentRequiresDescription', + data: { directive, minimumDescriptionLength }, }); } else if (format && !format.test(description)) { context.report({ - data: { directive, format: format.source }, node: comment, messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', + data: { directive, format: format.source }, }); } } diff --git a/packages/eslint-plugin/src/rules/ban-tslint-comment.ts b/packages/eslint-plugin/src/rules/ban-tslint-comment.ts index 4c84981a9fa3..48be960677ca 100644 --- a/packages/eslint-plugin/src/rules/ban-tslint-comment.ts +++ b/packages/eslint-plugin/src/rules/ban-tslint-comment.ts @@ -23,11 +23,11 @@ export default createRule({ description: 'Disallow `// tslint:` comments', recommended: 'stylistic', }, + fixable: 'code', messages: { commentDetected: 'tslint comment detected: "{{ text }}"', }, schema: [], - fixable: 'code', }, defaultOptions: [], create: context => { @@ -37,9 +37,9 @@ export default createRule({ comments.forEach(c => { if (ENABLE_DISABLE_REGEX.test(c.value)) { context.report({ - data: { text: toText(c.value, c.type) }, node: c, messageId: 'commentDetected', + data: { text: toText(c.value, c.type) }, fix(fixer) { const rangeStart = context.sourceCode.getIndexFromLoc({ column: c.loc.start.column > 0 ? c.loc.start.column - 1 : 0, diff --git a/packages/eslint-plugin/src/rules/class-literal-property-style.ts b/packages/eslint-plugin/src/rules/class-literal-property-style.ts index 9f02142f4d6e..dd1132f3b34d 100644 --- a/packages/eslint-plugin/src/rules/class-literal-property-style.ts +++ b/packages/eslint-plugin/src/rules/class-literal-property-style.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { @@ -23,8 +24,8 @@ interface NodeWithModifiers { } interface PropertiesInfo { - properties: TSESTree.PropertyDefinition[]; excludeSet: Set; + properties: TSESTree.PropertyDefinition[]; } const printNodeModifiers = ( @@ -82,13 +83,13 @@ export default createRule({ function enterClassBody(): void { propertiesInfoStack.push({ - properties: [], excludeSet: new Set(), + properties: [], }); } function exitClassBody(): void { - const { properties, excludeSet } = nullThrows( + const { excludeSet, properties } = nullThrows( propertiesInfoStack.pop(), 'Stack should exist on class exit', ); diff --git a/packages/eslint-plugin/src/rules/class-methods-use-this.ts b/packages/eslint-plugin/src/rules/class-methods-use-this.ts index 4b369d7a51be..1d3beff94d25 100644 --- a/packages/eslint-plugin/src/rules/class-methods-use-this.ts +++ b/packages/eslint-plugin/src/rules/class-methods-use-this.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { @@ -10,10 +11,10 @@ import { type Options = [ { - exceptMethods?: string[]; enforceForClassFields?: boolean; + exceptMethods?: string[]; + ignoreClassesThatImplementAnInterface?: 'public-fields' | boolean; ignoreOverrideMethods?: boolean; - ignoreClassesThatImplementAnInterface?: boolean | 'public-fields'; }, ]; type MessageIds = 'missingThis'; @@ -27,10 +28,20 @@ export default createRule({ extendsBaseRule: true, requiresTypeChecking: false, }, + messages: { + missingThis: "Expected 'this' to be used by class {{name}}.", + }, schema: [ { type: 'object', + additionalProperties: false, properties: { + enforceForClassFields: { + type: 'boolean', + default: true, + description: + 'Enforces that functions used as instance field initializers utilize `this`', + }, exceptMethods: { type: 'array', description: @@ -39,17 +50,9 @@ export default createRule({ type: 'string', }, }, - enforceForClassFields: { - type: 'boolean', - description: - 'Enforces that functions used as instance field initializers utilize `this`', - default: true, - }, - ignoreOverrideMethods: { - type: 'boolean', - description: 'Ignore members marked with the `override` modifier', - }, ignoreClassesThatImplementAnInterface: { + description: + 'Ignore classes that specifically implement some interface', oneOf: [ { type: 'boolean', @@ -57,21 +60,19 @@ export default createRule({ }, { type: 'string', - enum: ['public-fields'], description: 'Ignore only the public fields of classes that implement an interface', + enum: ['public-fields'], }, ], - description: - 'Ignore classes that specifically implement some interface', + }, + ignoreOverrideMethods: { + type: 'boolean', + description: 'Ignore members marked with the `override` modifier', }, }, - additionalProperties: false, }, ], - messages: { - missingThis: "Expected 'this' to be used by class {{name}}.", - }, }, defaultOptions: [ { @@ -95,14 +96,14 @@ export default createRule({ const exceptMethods = new Set(exceptMethodsRaw); type Stack = | { - member: null; class: null; + member: null; parent: Stack | undefined; usesThis: boolean; } | { - member: TSESTree.MethodDefinition | TSESTree.PropertyDefinition; class: TSESTree.ClassDeclaration | TSESTree.ClassExpression; + member: TSESTree.MethodDefinition | TSESTree.PropertyDefinition; parent: Stack | undefined; usesThis: boolean; }; @@ -113,17 +114,17 @@ export default createRule({ ): void { if (member?.parent.type === AST_NODE_TYPES.ClassBody) { stack = { - member, class: member.parent.parent, - usesThis: false, + member, parent: stack, + usesThis: false, }; } else { stack = { - member: null, class: null, - usesThis: false, + member: null, parent: stack, + usesThis: false, }; } } @@ -213,8 +214,8 @@ export default createRule({ if (isIncludedInstanceMethod(stackContext.member)) { context.report({ - node, loc: getFunctionHeadLoc(node, context.sourceCode), + node, messageId: 'missingThis', data: { name: getFunctionNameWithKind(node), @@ -256,12 +257,12 @@ export default createRule({ /* * Class field value are implicit functions. */ - 'PropertyDefinition > *.key:exit'(): void { - pushContext(); - }, 'PropertyDefinition:exit'(): void { popContext(); }, + 'PropertyDefinition > *.key:exit'(): void { + pushContext(); + }, /* * Class static blocks are implicit functions. They aren't required to use `this`, diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index c4a110c7aa86..f3033536b653 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; @@ -15,13 +16,13 @@ export default createRule({ 'Enforce specifying generic type arguments on type annotation or constructor name of a constructor call', recommended: 'stylistic', }, + fixable: 'code', messages: { - preferTypeAnnotation: - 'The generic type arguments should be specified as part of the type annotation.', preferConstructor: 'The generic type arguments should be specified as part of the constructor type arguments.', + preferTypeAnnotation: + 'The generic type arguments should be specified as part of the type annotation.', }, - fixable: 'code', schema: [ { type: 'string', @@ -75,7 +76,7 @@ export default createRule({ } if (mode === 'type-annotation') { if (!lhs && rhs.typeArguments) { - const { typeArguments, callee } = rhs; + const { callee, typeArguments } = rhs; const typeAnnotation = context.sourceCode.getText(callee) + context.sourceCode.getText(typeArguments); diff --git a/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts index f0f91cc32b8e..1908d274f0ff 100644 --- a/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts +++ b/packages/eslint-plugin/src/rules/consistent-indexed-object-style.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -14,11 +15,11 @@ export default createRule({ description: 'Require or disallow the `Record` type', recommended: 'stylistic', }, + fixable: 'code', messages: { - preferRecord: 'A record is preferred over an index signature.', preferIndexSignature: 'An index signature is preferred over a record.', + preferRecord: 'A record is preferred over an index signature.', }, - fixable: 'code', schema: [ { type: 'string', @@ -122,10 +123,6 @@ export default createRule({ }, }), ...(mode === 'record' && { - TSTypeLiteral(node): void { - const parent = findParentDeclaration(node); - checkMembers(node.members, node, parent?.id, '', ''); - }, TSInterfaceDeclaration(node): void { let genericTypes = ''; @@ -144,6 +141,10 @@ export default createRule({ !node.extends.length, ); }, + TSTypeLiteral(node): void { + const parent = findParentDeclaration(node); + checkMembers(node.members, node, parent?.id, '', ''); + }, }), }; }, diff --git a/packages/eslint-plugin/src/rules/consistent-return.ts b/packages/eslint-plugin/src/rules/consistent-return.ts index 5d4cc3fb9256..44bd653a56e0 100644 --- a/packages/eslint-plugin/src/rules/consistent-return.ts +++ b/packages/eslint-plugin/src/rules/consistent-return.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -6,6 +7,7 @@ import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule, getParserServices, isTypeFlagSet } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -15,9 +17,9 @@ type Options = InferOptionsTypeFromRule; type MessageIds = InferMessageIdsTypeFromRule; type FunctionNode = + | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.ArrowFunctionExpression; + | TSESTree.FunctionExpression; export default createRule({ name: 'consistent-return', @@ -30,8 +32,8 @@ export default createRule({ requiresTypeChecking: true, }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, messages: baseRule.meta.messages, + schema: baseRule.meta.schema, }, defaultOptions: [{ treatUndefinedAsUnspecified: false }], create(context, [options]) { @@ -86,6 +88,11 @@ export default createRule({ return { ...rules, + ArrowFunctionExpression: enterFunction, + 'ArrowFunctionExpression:exit'(node): void { + exitFunction(); + rules['ArrowFunctionExpression:exit'](node); + }, FunctionDeclaration: enterFunction, 'FunctionDeclaration:exit'(node): void { exitFunction(); @@ -96,11 +103,6 @@ export default createRule({ exitFunction(); rules['FunctionExpression:exit'](node); }, - ArrowFunctionExpression: enterFunction, - 'ArrowFunctionExpression:exit'(node): void { - exitFunction(); - rules['ArrowFunctionExpression:exit'](node); - }, ReturnStatement(node): void { const functionNode = getCurrentFunction(); if ( diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 764978efc1a9..037a980d17af 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -23,7 +24,7 @@ export type MessageIds = type OptUnion = | { assertionStyle: 'angle-bracket' | 'as'; - objectLiteralTypeAssertions?: 'allow-as-parameter' | 'allow' | 'never'; + objectLiteralTypeAssertions?: 'allow' | 'allow-as-parameter' | 'never'; } | { assertionStyle: 'never'; @@ -34,53 +35,53 @@ export default createRule({ name: 'consistent-type-assertions', meta: { type: 'suggestion', - fixable: 'code', - hasSuggestions: true, docs: { description: 'Enforce consistent usage of type assertions', recommended: 'stylistic', }, + fixable: 'code', + hasSuggestions: true, messages: { - as: "Use 'as {{cast}}' instead of '<{{cast}}>'.", 'angle-bracket': "Use '<{{cast}}>' instead of 'as {{cast}}'.", + as: "Use 'as {{cast}}' instead of '<{{cast}}>'.", never: 'Do not use any type assertions.', - unexpectedObjectTypeAssertion: 'Always prefer const x: T = { ... }.', replaceObjectTypeAssertionWithAnnotation: 'Use const x: {{cast}} = { ... } instead.', replaceObjectTypeAssertionWithSatisfies: 'Use const x = { ... } satisfies {{cast}} instead.', + unexpectedObjectTypeAssertion: 'Always prefer const x: T = { ... }.', }, schema: [ { oneOf: [ { type: 'object', + additionalProperties: false, properties: { assertionStyle: { - description: 'The expected assertion style to enforce.', type: 'string', + description: 'The expected assertion style to enforce.', enum: ['never'], }, }, - additionalProperties: false, required: ['assertionStyle'], }, { type: 'object', + additionalProperties: false, properties: { assertionStyle: { - description: 'The expected assertion style to enforce.', type: 'string', + description: 'The expected assertion style to enforce.', enum: ['as', 'angle-bracket'], }, objectLiteralTypeAssertions: { + type: 'string', description: 'Whether to always prefer type declarations for object literals used as variable initializers, rather than type assertions.', - type: 'string', enum: ['allow', 'allow-as-parameter', 'never'], }, }, - additionalProperties: false, required: ['assertionStyle'], }, ], @@ -263,16 +264,16 @@ export default createRule({ } return { - TSTypeAssertion(node): void { - if (options.assertionStyle !== 'angle-bracket') { + TSAsExpression(node): void { + if (options.assertionStyle !== 'as') { reportIncorrectAssertionType(node); return; } checkExpression(node); }, - TSAsExpression(node): void { - if (options.assertionStyle !== 'as') { + TSTypeAssertion(node): void { + if (options.assertionStyle !== 'angle-bracket') { reportIncorrectAssertionType(node); return; } diff --git a/packages/eslint-plugin/src/rules/consistent-type-definitions.ts b/packages/eslint-plugin/src/rules/consistent-type-definitions.ts index 45b156fdf0ee..2505b7c54262 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-definitions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-definitions.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -12,6 +13,7 @@ export default createRule({ 'Enforce type definitions to consistently use either `interface` or `type`', recommended: 'stylistic', }, + fixable: 'code', messages: { interfaceOverType: 'Use an `interface` instead of a `type`.', typeOverInterface: 'Use a `type` instead of an `interface`.', @@ -22,7 +24,6 @@ export default createRule({ enum: ['interface', 'type'], }, ], - fixable: 'code', }, defaultOptions: ['interface'], create(context, [option]) { diff --git a/packages/eslint-plugin/src/rules/consistent-type-exports.ts b/packages/eslint-plugin/src/rules/consistent-type-exports.ts index dd9d573fcfa9..4acd22ecf746 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-exports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-exports.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -20,17 +21,17 @@ type Options = [ ]; interface SourceExports { - source: string; reportValueExports: ReportValueExport[]; + source: string; typeOnlyNamedExport: TSESTree.ExportNamedDeclaration | null; valueOnlyNamedExport: TSESTree.ExportNamedDeclaration | null; } interface ReportValueExport { + inlineTypeSpecifiers: TSESTree.ExportSpecifier[]; node: TSESTree.ExportNamedDeclaration; typeBasedSpecifiers: TSESTree.ExportSpecifier[]; valueSpecifiers: TSESTree.ExportSpecifier[]; - inlineTypeSpecifiers: TSESTree.ExportSpecifier[]; } type MessageIds = @@ -46,28 +47,28 @@ export default createRule({ description: 'Enforce consistent usage of type exports', requiresTypeChecking: true, }, + fixable: 'code', messages: { - typeOverValue: - 'All exports in the declaration are only used as types. Use `export type`.', - singleExportIsType: - 'Type export {{exportNames}} is not a value and should be exported using `export type`.', multipleExportsAreTypes: 'Type exports {{exportNames}} are not values and should be exported using `export type`.', + singleExportIsType: + 'Type export {{exportNames}} is not a value and should be exported using `export type`.', + typeOverValue: + 'All exports in the declaration are only used as types. Use `export type`.', }, schema: [ { type: 'object', + additionalProperties: false, properties: { fixMixedExportsWithInlineTypeSpecifier: { + type: 'boolean', description: 'Whether the rule will autofix "mixed" export cases using TS inline type specifiers.', - type: 'boolean', }, }, - additionalProperties: false, }, ], - fixable: 'code', }, defaultOptions: [ { @@ -193,8 +194,8 @@ export default createRule({ const source = getSourceFromExport(node) ?? 'undefined'; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const sourceExports = (sourceExportsMap[source] ||= { - source, reportValueExports: [], + source, typeOnlyNamedExport: null, valueOnlyNamedExport: null, }); @@ -244,9 +245,9 @@ export default createRule({ ) { sourceExports.reportValueExports.push({ node, + inlineTypeSpecifiers, typeBasedSpecifiers, valueSpecifiers, - inlineTypeSpecifiers, }); } }, @@ -374,7 +375,7 @@ function* fixSeparateNamedExports( sourceCode: Readonly, report: ReportValueExport, ): IterableIterator { - const { node, typeBasedSpecifiers, inlineTypeSpecifiers, valueSpecifiers } = + const { node, inlineTypeSpecifiers, typeBasedSpecifiers, valueSpecifiers } = report; const typeSpecifiers = [...typeBasedSpecifiers, ...inlineTypeSpecifiers]; const source = getSourceFromExport(node); diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts index 8a618faf89c1..acb711848f1e 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts @@ -1,7 +1,8 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { RuleListener } from '@typescript-eslint/utils/eslint-utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + import { createRule, formatWordList, @@ -20,35 +21,35 @@ type FixStyle = 'inline-type-imports' | 'separate-type-imports'; type Options = [ { - prefer?: Prefer; disallowTypeAnnotations?: boolean; fixStyle?: FixStyle; + prefer?: Prefer; }, ]; interface SourceImports { - source: string; reportValueImports: ReportValueImport[]; + source: string; // ImportDeclaration for type-only import only with named imports. typeOnlyNamedImport: TSESTree.ImportDeclaration | null; - // ImportDeclaration for value-only import only with named imports. - valueOnlyNamedImport: TSESTree.ImportDeclaration | null; // ImportDeclaration for value-only import only with default imports and/or named imports. valueImport: TSESTree.ImportDeclaration | null; + // ImportDeclaration for value-only import only with named imports. + valueOnlyNamedImport: TSESTree.ImportDeclaration | null; } interface ReportValueImport { + inlineTypeSpecifiers: TSESTree.ImportSpecifier[]; node: TSESTree.ImportDeclaration; typeSpecifiers: TSESTree.ImportClause[]; // It has at least one element. - valueSpecifiers: TSESTree.ImportClause[]; unusedSpecifiers: TSESTree.ImportClause[]; - inlineTypeSpecifiers: TSESTree.ImportSpecifier[]; + valueSpecifiers: TSESTree.ImportClause[]; } type MessageIds = - | 'typeOverValue' - | 'someImportsAreOnlyTypes' | 'avoidImportType' - | 'noImportTypeAnnotations'; + | 'noImportTypeAnnotations' + | 'someImportsAreOnlyTypes' + | 'typeOverValue'; export default createRule({ name: 'consistent-type-imports', meta: { @@ -56,45 +57,45 @@ export default createRule({ docs: { description: 'Enforce consistent usage of type imports', }, + fixable: 'code', messages: { - typeOverValue: - 'All imports in the declaration are only used as types. Use `import type`.', - someImportsAreOnlyTypes: 'Imports {{typeImports}} are only used as type.', avoidImportType: 'Use an `import` instead of an `import type`.', noImportTypeAnnotations: '`import()` type annotations are forbidden.', + someImportsAreOnlyTypes: 'Imports {{typeImports}} are only used as type.', + typeOverValue: + 'All imports in the declaration are only used as types. Use `import type`.', }, schema: [ { type: 'object', + additionalProperties: false, properties: { disallowTypeAnnotations: { + type: 'boolean', description: 'Whether to disallow type imports in type annotations (`import()`).', - type: 'boolean', }, fixStyle: { + type: 'string', description: 'The expected type modifier to be added when an import is detected as used only in the type position.', - type: 'string', enum: ['separate-type-imports', 'inline-type-imports'], }, prefer: { - description: 'The expected import kind for type-only imports.', type: 'string', + description: 'The expected import kind for type-only imports.', enum: ['type-imports', 'no-type-imports'], }, }, - additionalProperties: false, }, ], - fixable: 'code', }, defaultOptions: [ { - prefer: 'type-imports', disallowTypeAnnotations: true, fixStyle: 'separate-type-imports', + prefer: 'type-imports', }, ], @@ -164,11 +165,11 @@ export default createRule({ const source = node.source.value; // sourceImports is the object containing all the specifics for a particular import source, type or value sourceImportsMap[source] ??= { - source, reportValueImports: [], // if there is a mismatch where type importKind but value specifiers + source, typeOnlyNamedImport: null, // if only type imports - valueOnlyNamedImport: null, // if only value imports with named specifiers valueImport: null, // if only value imports + valueOnlyNamedImport: null, // if only value imports with named specifiers }; const sourceImports = sourceImportsMap[source]; if (node.importKind === 'type') { @@ -294,10 +295,10 @@ export default createRule({ if (node.importKind === 'value' && typeSpecifiers.length) { sourceImports.reportValueImports.push({ node, + inlineTypeSpecifiers, typeSpecifiers, - valueSpecifiers, unusedSpecifiers, - inlineTypeSpecifiers, + valueSpecifiers, }); } }, @@ -379,8 +380,8 @@ export default createRule({ ); const message = ((): { - messageId: MessageIds; data: Record; + messageId: MessageIds; } => { const typeImports = formatWordList(importNames); @@ -420,8 +421,8 @@ export default createRule({ function classifySpecifier(node: TSESTree.ImportDeclaration): { defaultSpecifier: TSESTree.ImportDefaultSpecifier | null; - namespaceSpecifier: TSESTree.ImportNamespaceSpecifier | null; namedSpecifiers: TSESTree.ImportSpecifier[]; + namespaceSpecifier: TSESTree.ImportNamespaceSpecifier | null; } { const defaultSpecifier = node.specifiers[0].type === AST_NODE_TYPES.ImportDefaultSpecifier @@ -438,8 +439,8 @@ export default createRule({ ); return { defaultSpecifier, - namespaceSpecifier, namedSpecifiers, + namespaceSpecifier, }; } @@ -452,13 +453,13 @@ export default createRule({ subsetNamedSpecifiers: TSESTree.ImportSpecifier[], allNamedSpecifiers: TSESTree.ImportSpecifier[], ): { - typeNamedSpecifiersText: string; removeTypeNamedSpecifiers: TSESLint.RuleFix[]; + typeNamedSpecifiersText: string; } { if (allNamedSpecifiers.length === 0) { return { - typeNamedSpecifiersText: '', removeTypeNamedSpecifiers: [], + typeNamedSpecifiersText: '', }; } const typeNamedSpecifiersTexts: string[] = []; @@ -525,8 +526,8 @@ export default createRule({ } } return { - typeNamedSpecifiersText: typeNamedSpecifiersTexts.join(','), removeTypeNamedSpecifiers, + typeNamedSpecifiersText: typeNamedSpecifiersTexts.join(','), }; } @@ -537,8 +538,8 @@ export default createRule({ namedSpecifierGroup: TSESTree.ImportSpecifier[], allNamedSpecifiers: TSESTree.ImportSpecifier[], ): { - textRange: TSESTree.Range; removeRange: TSESTree.Range; + textRange: TSESTree.Range; } { const first = namedSpecifierGroup[0]; const last = namedSpecifierGroup[namedSpecifierGroup.length - 1]; @@ -567,8 +568,8 @@ export default createRule({ } return { - textRange, removeRange, + textRange, }; } @@ -657,7 +658,7 @@ export default createRule({ ): IterableIterator { const { node } = report; - const { defaultSpecifier, namespaceSpecifier, namedSpecifiers } = + const { defaultSpecifier, namedSpecifiers, namespaceSpecifier } = classifySpecifier(node); if (namespaceSpecifier && !defaultSpecifier) { diff --git a/packages/eslint-plugin/src/rules/default-param-last.ts b/packages/eslint-plugin/src/rules/default-param-last.ts index 1340ecfa17b1..1d120720d26c 100644 --- a/packages/eslint-plugin/src/rules/default-param-last.ts +++ b/packages/eslint-plugin/src/rules/default-param-last.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -11,10 +12,10 @@ export default createRule({ description: 'Enforce default parameters to be last', extendsBaseRule: true, }, - schema: [], messages: { shouldBeLast: 'Default parameters should be last.', }, + schema: [], }, defaultOptions: [], create(context) { diff --git a/packages/eslint-plugin/src/rules/dot-notation.ts b/packages/eslint-plugin/src/rules/dot-notation.ts index f7b1c612288c..ae0dd908f6f1 100644 --- a/packages/eslint-plugin/src/rules/dot-notation.ts +++ b/packages/eslint-plugin/src/rules/dot-notation.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -7,6 +8,7 @@ import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule, getModifiers, getParserServices } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -21,57 +23,57 @@ export default createRule({ type: 'suggestion', docs: { description: 'Enforce dot notation whenever possible', - recommended: 'stylistic', extendsBaseRule: true, + recommended: 'stylistic', requiresTypeChecking: true, }, + fixable: baseRule.meta.fixable, + hasSuggestions: baseRule.meta.hasSuggestions, + messages: baseRule.meta.messages, schema: [ { type: 'object', + additionalProperties: false, properties: { + allowIndexSignaturePropertyAccess: { + type: 'boolean', + default: false, + description: + 'Whether to allow accessing properties matching an index signature with array notation.', + }, allowKeywords: { - description: 'Whether to allow keywords such as ["class"]`.', type: 'boolean', default: true, + description: 'Whether to allow keywords such as ["class"]`.', }, allowPattern: { - description: 'Regular expression of names to allow.', type: 'string', default: '', + description: 'Regular expression of names to allow.', }, allowPrivateClassPropertyAccess: { - description: - 'Whether to allow accessing class members marked as `private` with array notation.', type: 'boolean', default: false, + description: + 'Whether to allow accessing class members marked as `private` with array notation.', }, allowProtectedClassPropertyAccess: { - description: - 'Whether to allow accessing class members marked as `protected` with array notation.', type: 'boolean', default: false, - }, - allowIndexSignaturePropertyAccess: { description: - 'Whether to allow accessing properties matching an index signature with array notation.', - type: 'boolean', - default: false, + 'Whether to allow accessing class members marked as `protected` with array notation.', }, }, - additionalProperties: false, }, ], - fixable: baseRule.meta.fixable, - hasSuggestions: baseRule.meta.hasSuggestions, - messages: baseRule.meta.messages, }, defaultOptions: [ { - allowPrivateClassPropertyAccess: false, - allowProtectedClassPropertyAccess: false, allowIndexSignaturePropertyAccess: false, allowKeywords: true, allowPattern: '', + allowPrivateClassPropertyAccess: false, + allowProtectedClassPropertyAccess: false, }, ], create(context, [options]) { diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts index 9904dc2e5336..186241cb72a2 100644 --- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -1,8 +1,10 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule, nullThrows } from '../util'; import type { FunctionInfo } from '../util/explicitReturnTypeUtils'; + +import { createRule, nullThrows } from '../util'; import { ancestorHasReturnType, checkFunctionReturnType, @@ -11,14 +13,14 @@ import { type Options = [ { - allowExpressions?: boolean; - allowTypedFunctionExpressions?: boolean; - allowHigherOrderFunctions?: boolean; - allowDirectConstAssertionInArrowFunctions?: boolean; allowConciseArrowFunctionExpressionsStartingWithVoid?: boolean; - allowFunctionsWithoutTypeParameters?: boolean; + allowDirectConstAssertionInArrowFunctions?: boolean; allowedNames?: string[]; + allowExpressions?: boolean; + allowFunctionsWithoutTypeParameters?: boolean; + allowHigherOrderFunctions?: boolean; allowIIFEs?: boolean; + allowTypedFunctionExpressions?: boolean; }, ]; type MessageIds = 'missingReturnType'; @@ -42,65 +44,65 @@ export default createRule({ schema: [ { type: 'object', + additionalProperties: false, properties: { allowConciseArrowFunctionExpressionsStartingWithVoid: { + type: 'boolean', description: 'Whether to allow arrow functions that start with the `void` keyword.', - type: 'boolean', }, - allowExpressions: { - description: - 'Whether to ignore function expressions (functions which are not part of a declaration).', + allowDirectConstAssertionInArrowFunctions: { type: 'boolean', - }, - allowHigherOrderFunctions: { description: - 'Whether to ignore functions immediately returning another function expression.', - type: 'boolean', + 'Whether to ignore arrow functions immediately returning a `as const` value.', }, - allowTypedFunctionExpressions: { + allowedNames: { + type: 'array', description: - 'Whether to ignore type annotations on the variable of function expressions.', - type: 'boolean', + 'An array of function/method names that will not have their arguments or return values checked.', + items: { + type: 'string', + }, }, - allowDirectConstAssertionInArrowFunctions: { - description: - 'Whether to ignore arrow functions immediately returning a `as const` value.', + allowExpressions: { type: 'boolean', + description: + 'Whether to ignore function expressions (functions which are not part of a declaration).', }, allowFunctionsWithoutTypeParameters: { + type: 'boolean', description: "Whether to ignore functions that don't have generic type parameters.", - type: 'boolean', }, - allowedNames: { + allowHigherOrderFunctions: { + type: 'boolean', description: - 'An array of function/method names that will not have their arguments or return values checked.', - items: { - type: 'string', - }, - type: 'array', + 'Whether to ignore functions immediately returning another function expression.', }, allowIIFEs: { + type: 'boolean', description: 'Whether to ignore immediately invoked function expressions (IIFEs).', + }, + allowTypedFunctionExpressions: { type: 'boolean', + description: + 'Whether to ignore type annotations on the variable of function expressions.', }, }, - additionalProperties: false, }, ], }, defaultOptions: [ { - allowExpressions: false, - allowTypedFunctionExpressions: true, - allowHigherOrderFunctions: true, - allowDirectConstAssertionInArrowFunctions: true, allowConciseArrowFunctionExpressionsStartingWithVoid: false, - allowFunctionsWithoutTypeParameters: false, + allowDirectConstAssertionInArrowFunctions: true, allowedNames: [], + allowExpressions: false, + allowFunctionsWithoutTypeParameters: false, + allowHigherOrderFunctions: true, allowIIFEs: false, + allowTypedFunctionExpressions: true, }, ], create(context, [options]) { @@ -219,8 +221,8 @@ export default createRule({ checkFunctionReturnType(info, options, context.sourceCode, loc => context.report({ - node, loc, + node, messageId: 'missingReturnType', }), ); @@ -230,7 +232,6 @@ export default createRule({ 'ArrowFunctionExpression, FunctionExpression, FunctionDeclaration': enterFunction, 'ArrowFunctionExpression:exit': exitFunctionExpression, - 'FunctionExpression:exit': exitFunctionExpression, 'FunctionDeclaration:exit'(node): void { const info = popFunctionInfo('function declaration'); if (isAllowedFunction(node)) { @@ -242,12 +243,13 @@ export default createRule({ checkFunctionReturnType(info, options, context.sourceCode, loc => context.report({ - node, loc, + node, messageId: 'missingReturnType', }), ); }, + 'FunctionExpression:exit': exitFunctionExpression, ReturnStatement(node): void { functionInfoStack.at(-1)?.returns.push(node); }, diff --git a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts index 3bf70eb8bb74..b03a75cc210e 100644 --- a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts +++ b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { @@ -25,8 +26,8 @@ interface Config { accessors?: AccessibilityLevel; constructors?: AccessibilityLevel; methods?: AccessibilityLevel; - properties?: AccessibilityLevel; parameterProperties?: AccessibilityLevel; + properties?: AccessibilityLevel; }; } @@ -40,7 +41,6 @@ type MessageIds = export default createRule({ name: 'explicit-member-accessibility', meta: { - hasSuggestions: true, type: 'problem', docs: { description: @@ -48,61 +48,62 @@ export default createRule({ // too opinionated to be recommended }, fixable: 'code', + hasSuggestions: true, messages: { + addExplicitAccessibility: "Add '{{ type }}' accessibility modifier", missingAccessibility: 'Missing accessibility modifier on {{type}} {{name}}.', unwantedPublicAccessibility: 'Public accessibility modifier on {{type}} {{name}}.', - addExplicitAccessibility: "Add '{{ type }}' accessibility modifier", }, schema: [ { + type: 'object', $defs: { accessibilityLevel: { oneOf: [ { type: 'string', - enum: ['explicit'], description: 'Always require an accessor.', + enum: ['explicit'], }, { type: 'string', - enum: ['no-public'], description: 'Require an accessor except when public.', + enum: ['no-public'], }, { type: 'string', - enum: ['off'], description: 'Never check whether there is an accessor.', + enum: ['off'], }, ], }, }, - type: 'object', + additionalProperties: false, properties: { accessibility: { $ref: '#/items/0/$defs/accessibilityLevel' }, + ignoredMethodNames: { + type: 'array', + description: 'Specific method names that may be ignored.', + items: { + type: 'string', + }, + }, overrides: { type: 'object', + additionalProperties: false, properties: { accessors: { $ref: '#/items/0/$defs/accessibilityLevel' }, constructors: { $ref: '#/items/0/$defs/accessibilityLevel' }, methods: { $ref: '#/items/0/$defs/accessibilityLevel' }, - properties: { $ref: '#/items/0/$defs/accessibilityLevel' }, parameterProperties: { $ref: '#/items/0/$defs/accessibilityLevel', }, - }, - additionalProperties: false, - }, - ignoredMethodNames: { - description: 'Specific method names that may be ignored.', - type: 'array', - items: { - type: 'string', + properties: { $ref: '#/items/0/$defs/accessibilityLevel' }, }, }, }, - additionalProperties: false, }, ], }, @@ -164,8 +165,8 @@ export default createRule({ loc: rangeToLoc(context.sourceCode, publicKeyword.range), messageId: 'unwantedPublicAccessibility', data: { - type: nodeType, name: methodName, + type: nodeType, }, fix: fixer => fixer.removeRange(publicKeyword.rangeToRemove), }); @@ -174,8 +175,8 @@ export default createRule({ loc: getMemberHeadLoc(context.sourceCode, methodDefinition), messageId: 'missingAccessibility', data: { - type: nodeType, name: methodName, + type: nodeType, }, suggest: getMissingAccessibilitySuggestions(methodDefinition), }); @@ -299,8 +300,8 @@ export default createRule({ loc: rangeToLoc(context.sourceCode, publicKeywordRange.range), messageId: 'unwantedPublicAccessibility', data: { - type: nodeType, name: propertyName, + type: nodeType, }, fix: fixer => fixer.removeRange(publicKeywordRange.rangeToRemove), }); @@ -312,8 +313,8 @@ export default createRule({ loc: getMemberHeadLoc(context.sourceCode, propertyDefinition), messageId: 'missingAccessibility', data: { - type: nodeType, name: propertyName, + type: nodeType, }, suggest: getMissingAccessibilitySuggestions(propertyDefinition), }); @@ -353,8 +354,8 @@ export default createRule({ ), messageId: 'missingAccessibility', data: { - type: nodeType, name: nodeName, + type: nodeType, }, suggest: getMissingAccessibilitySuggestions(node), }); @@ -368,8 +369,8 @@ export default createRule({ loc: rangeToLoc(context.sourceCode, publicKeyword.range), messageId: 'unwantedPublicAccessibility', data: { - type: nodeType, name: nodeName, + type: nodeType, }, fix: fixer => fixer.removeRange(publicKeyword.rangeToRemove), }); diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts index 2bcd6cf4df43..ee8f691b7695 100644 --- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts +++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts @@ -1,13 +1,15 @@ -import { DefinitionType } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; + +import { DefinitionType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule, isFunction, isStaticMemberAccessOfValue } from '../util'; import type { FunctionExpression, FunctionInfo, FunctionNode, } from '../util/explicitReturnTypeUtils'; + +import { createRule, isFunction, isStaticMemberAccessOfValue } from '../util'; import { ancestorHasReturnType, checkFunctionExpressionReturnType, @@ -41,51 +43,51 @@ export default createRule({ "Require explicit return and argument types on exported functions' and classes' public class methods", }, messages: { - missingReturnType: 'Missing return type on function.', - missingArgType: "Argument '{{name}}' should be typed.", - missingArgTypeUnnamed: '{{type}} argument should be typed.', anyTypedArg: "Argument '{{name}}' should be typed with a non-any type.", anyTypedArgUnnamed: '{{type}} argument should be typed with a non-any type.', + missingArgType: "Argument '{{name}}' should be typed.", + missingArgTypeUnnamed: '{{type}} argument should be typed.', + missingReturnType: 'Missing return type on function.', }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowArgumentsExplicitlyTypedAsAny: { + type: 'boolean', description: 'Whether to ignore arguments that are explicitly typed as `any`.', - type: 'boolean', }, allowDirectConstAssertionInArrowFunctions: { + type: 'boolean', description: [ 'Whether to ignore return type annotations on body-less arrow functions that return an `as const` type assertion.', 'You must still type the parameters of the function.', ].join('\n'), - type: 'boolean', }, allowedNames: { + type: 'array', description: 'An array of function/method names that will not have their arguments or return values checked.', items: { type: 'string', }, - type: 'array', }, allowHigherOrderFunctions: { + type: 'boolean', description: [ 'Whether to ignore return type annotations on functions immediately returning another function expression.', 'You must still type the parameters of the function.', ].join('\n'), - type: 'boolean', }, allowTypedFunctionExpressions: { + type: 'boolean', description: 'Whether to ignore type annotations on the variable of a function expression.', - type: 'boolean', }, }, - additionalProperties: false, }, ], }, @@ -139,6 +141,9 @@ export default createRule({ */ return { + 'ArrowFunctionExpression, FunctionDeclaration, FunctionExpression': + enterFunction, + 'ArrowFunctionExpression:exit': exitFunction, 'ExportDefaultDeclaration:exit'(node): void { checkNode(node.declaration); }, @@ -153,12 +158,6 @@ export default createRule({ } } }, - 'TSExportAssignment:exit'(node): void { - checkNode(node.expression); - }, - 'ArrowFunctionExpression, FunctionDeclaration, FunctionExpression': - enterFunction, - 'ArrowFunctionExpression:exit': exitFunction, 'FunctionDeclaration:exit': exitFunction, 'FunctionExpression:exit': exitFunction, 'Program:exit'(): void { @@ -172,6 +171,9 @@ export default createRule({ const current = functionStack[functionStack.length - 1]; functionReturnsMap.get(current)?.push(node); }, + 'TSExportAssignment:exit'(node): void { + checkNode(node.expression); + }, }; function checkParameters( @@ -321,9 +323,9 @@ export default createRule({ // cases we don't care about in this rule if ( [ + DefinitionType.CatchClause, DefinitionType.ImplicitGlobalVariable, DefinitionType.ImportBinding, - DefinitionType.CatchClause, DefinitionType.Parameter, ].includes(definition.type) ) { @@ -456,8 +458,8 @@ export default createRule({ context.sourceCode, loc => { context.report({ - node, loc, + node, messageId: 'missingReturnType', }); }, @@ -485,8 +487,8 @@ export default createRule({ context.sourceCode, loc => { context.report({ - node, loc, + node, messageId: 'missingReturnType', }); }, diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 49d8bd67c5cb..72c62f3e0122 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -128,7 +128,7 @@ import unboundMethod from './unbound-method'; import unifiedSignatures from './unified-signatures'; import useUnknownInCatchCallbackVariable from './use-unknown-in-catch-callback-variable'; -export default { +const rules = { 'adjacent-overload-signatures': adjacentOverloadSignatures, 'array-type': arrayType, 'await-thenable': awaitThenable, @@ -258,3 +258,5 @@ export default { 'unified-signatures': unifiedSignatures, 'use-unknown-in-catch-callback-variable': useUnknownInCatchCallbackVariable, } satisfies Linter.PluginRules; + +export = rules; diff --git a/packages/eslint-plugin/src/rules/init-declarations.ts b/packages/eslint-plugin/src/rules/init-declarations.ts index 53ad84350e30..18629283c451 100644 --- a/packages/eslint-plugin/src/rules/init-declarations.ts +++ b/packages/eslint-plugin/src/rules/init-declarations.ts @@ -1,10 +1,12 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -23,8 +25,8 @@ export default createRule({ extendsBaseRule: true, }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, messages: baseRule.meta.messages, + schema: baseRule.meta.schema, }, defaultOptions: ['always'], create(context, [mode]) { diff --git a/packages/eslint-plugin/src/rules/max-params.ts b/packages/eslint-plugin/src/rules/max-params.ts index 680cea10a40c..fe566bff824a 100644 --- a/packages/eslint-plugin/src/rules/max-params.ts +++ b/packages/eslint-plugin/src/rules/max-params.ts @@ -1,17 +1,19 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; type FunctionLike = + | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.ArrowFunctionExpression; + | TSESTree.FunctionExpression; type FunctionRuleListener = (node: T) => void; @@ -29,34 +31,34 @@ export default createRule({ 'Enforce a maximum number of parameters in function definitions', extendsBaseRule: true, }, + messages: baseRule.meta.messages, schema: [ { type: 'object', + additionalProperties: false, properties: { + countVoidThis: { + type: 'boolean', + description: + 'Whether to count a `this` declaration when the type is `void`.', + }, max: { + type: 'integer', description: 'A maximum number of parameters in function definitions.', - type: 'integer', minimum: 0, }, maximum: { + type: 'integer', description: '(deprecated) A maximum number of parameters in function definitions.', - type: 'integer', minimum: 0, }, - countVoidThis: { - description: - 'Whether to count a `this` declaration when the type is `void`.', - type: 'boolean', - }, }, - additionalProperties: false, }, ], - messages: baseRule.meta.messages, }, - defaultOptions: [{ max: 3, countVoidThis: false }], + defaultOptions: [{ countVoidThis: false, max: 3 }], create(context, [{ countVoidThis }]) { const baseRules = baseRule.create(context); diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index fcc711a3aaa1..f845f1f1ae75 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -2,6 +2,7 @@ /* eslint-disable eslint-plugin/no-property-in-node */ import type { JSONSchema, TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import naturalCompare from 'natural-compare'; @@ -20,7 +21,6 @@ export type MessageIds = type ReadonlyType = 'readonly-field' | 'readonly-signature'; type MemberKind = - | ReadonlyType | 'accessor' | 'call-signature' | 'constructor' @@ -29,15 +29,16 @@ type MemberKind = | 'method' | 'set' | 'signature' - | 'static-initialization'; + | 'static-initialization' + | ReadonlyType; type DecoratedMemberKind = - | Exclude | 'accessor' | 'field' | 'get' | 'method' - | 'set'; + | 'set' + | Exclude; type NonCallableMemberKind = Exclude< MemberKind, @@ -46,10 +47,9 @@ type NonCallableMemberKind = Exclude< type MemberScope = 'abstract' | 'instance' | 'static'; -type Accessibility = TSESTree.Accessibility | '#private'; +type Accessibility = '#private' | TSESTree.Accessibility; type BaseMemberType = - | MemberKind | `${Accessibility}-${Exclude< MemberKind, 'readonly-signature' | 'signature' | 'static-initialization' @@ -57,34 +57,35 @@ type BaseMemberType = | `${Accessibility}-${MemberScope}-${NonCallableMemberKind}` | `${Accessibility}-decorated-${DecoratedMemberKind}` | `${MemberScope}-${NonCallableMemberKind}` - | `decorated-${DecoratedMemberKind}`; + | `decorated-${DecoratedMemberKind}` + | MemberKind; type MemberType = BaseMemberType | BaseMemberType[]; type AlphabeticalOrder = - | 'alphabetically-case-insensitive' | 'alphabetically' - | 'natural-case-insensitive' - | 'natural'; + | 'alphabetically-case-insensitive' + | 'natural' + | 'natural-case-insensitive'; -type Order = AlphabeticalOrder | 'as-written'; +type Order = 'as-written' | AlphabeticalOrder; interface SortedOrderConfig { - memberTypes?: MemberType[] | 'never'; + memberTypes?: 'never' | MemberType[]; optionalityOrder?: OptionalityOrder; order?: Order; } -type OrderConfig = MemberType[] | SortedOrderConfig | 'never'; +type OrderConfig = 'never' | MemberType[] | SortedOrderConfig; type Member = TSESTree.ClassElement | TSESTree.TypeElement; type OptionalityOrder = 'optional-first' | 'required-first'; export type Options = [ { - default?: OrderConfig; classes?: OrderConfig; classExpressions?: OrderConfig; + default?: OrderConfig; interfaces?: OrderConfig; typeLiterals?: OrderConfig; }, @@ -114,18 +115,18 @@ const arrayConfig = (memberTypes: string): JSONSchema.JSONSchema4 => ({ const objectConfig = (memberTypes: string): JSONSchema.JSONSchema4 => ({ type: 'object', + additionalProperties: false, properties: { memberTypes: { oneOf: [arrayConfig(memberTypes), neverConfig], }, - order: { - $ref: '#/items/0/$defs/orderOptions', - }, optionalityOrder: { $ref: '#/items/0/$defs/optionalityOrderOptions', }, + order: { + $ref: '#/items/0/$defs/orderOptions', + }, }, - additionalProperties: false, }); export const defaultOrder: MemberType[] = [ @@ -413,12 +414,12 @@ function getNodeType(node: Member): MemberKind | null { */ function getMemberRawName( member: - | TSESTree.MethodDefinition | TSESTree.AccessorProperty + | TSESTree.MethodDefinition | TSESTree.Property | TSESTree.PropertyDefinition - | TSESTree.TSAbstractMethodDefinition | TSESTree.TSAbstractAccessorProperty + | TSESTree.TSAbstractMethodDefinition | TSESTree.TSAbstractPropertyDefinition | TSESTree.TSMethodSignature | TSESTree.TSPropertySignature, @@ -722,15 +723,24 @@ export default createRule({ description: 'Require a consistent member declaration order', }, messages: { - incorrectOrder: - 'Member {{member}} should be declared before member {{beforeMember}}.', incorrectGroupOrder: 'Member {{name}} should be declared before all {{rank}} definitions.', + incorrectOrder: + 'Member {{member}} should be declared before member {{beforeMember}}.', incorrectRequiredMembersOrder: `Member {{member}} should be declared after all {{optionalOrRequired}} members.`, }, schema: [ { + type: 'object', $defs: { + allItems: { + type: 'string', + enum: allMemberTypes as string[], + }, + optionalityOrderOptions: { + type: 'string', + enum: ['optional-first', 'required-first'], + }, orderOptions: { type: 'string', enum: [ @@ -741,14 +751,6 @@ export default createRule({ 'natural-case-insensitive', ], }, - optionalityOrderOptions: { - type: 'string', - enum: ['optional-first', 'required-first'], - }, - allItems: { - type: 'string', - enum: allMemberTypes as string[], - }, typeItems: { type: 'string', enum: [ @@ -760,7 +762,7 @@ export default createRule({ 'constructor', ], }, - + // ajv is order-dependent; these configs must come last baseConfig: { oneOf: [ neverConfig, @@ -776,17 +778,17 @@ export default createRule({ ], }, }, - type: 'object', + additionalProperties: false, properties: { - default: { - $ref: '#/items/0/$defs/baseConfig', - }, classes: { $ref: '#/items/0/$defs/baseConfig', }, classExpressions: { $ref: '#/items/0/$defs/baseConfig', }, + default: { + $ref: '#/items/0/$defs/baseConfig', + }, interfaces: { $ref: '#/items/0/$defs/typesConfig', }, @@ -794,7 +796,6 @@ export default createRule({ $ref: '#/items/0/$defs/typesConfig', }, }, - additionalProperties: false, }, ], }, @@ -885,8 +886,8 @@ export default createRule({ node: member, messageId: 'incorrectOrder', data: { - member: name, beforeMember: previousName, + member: name, }, }); @@ -943,8 +944,8 @@ export default createRule({ const report = (member: Member): void => context.report({ - messageId: 'incorrectRequiredMembersOrder', loc: member.loc, + messageId: 'incorrectRequiredMembersOrder', data: { member: getMemberName(member, context.sourceCode), optionalOrRequired: @@ -1073,11 +1074,6 @@ export default createRule({ // https://github.com/typescript-eslint/typescript-eslint/issues/5439 /* eslint-disable @typescript-eslint/no-non-null-assertion */ return { - 'ClassDeclaration, FunctionDeclaration'(node): void { - if ('superClass' in node) { - // ... - } - }, ClassDeclaration(node): void { validateMembersOrder( node.body.body, @@ -1085,6 +1081,11 @@ export default createRule({ true, ); }, + 'ClassDeclaration, FunctionDeclaration'(node): void { + if ('superClass' in node) { + // ... + } + }, ClassExpression(node): void { validateMembersOrder( node.body.body, diff --git a/packages/eslint-plugin/src/rules/method-signature-style.ts b/packages/eslint-plugin/src/rules/method-signature-style.ts index 48207e5cc91a..1d7826402d3f 100644 --- a/packages/eslint-plugin/src/rules/method-signature-style.ts +++ b/packages/eslint-plugin/src/rules/method-signature-style.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/format.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/format.ts index d3db62399eaa..e8fdbdfd4f52 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/format.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/format.ts @@ -99,12 +99,12 @@ function validateUnderscores(name: string): boolean { const PredefinedFormatToCheckFunction: Readonly< Record boolean> > = { - [PredefinedFormats.PascalCase]: isPascalCase, - [PredefinedFormats.StrictPascalCase]: isStrictPascalCase, [PredefinedFormats.camelCase]: isCamelCase, + [PredefinedFormats.PascalCase]: isPascalCase, + [PredefinedFormats.snake_case]: isSnakeCase, [PredefinedFormats.strictCamelCase]: isStrictCamelCase, + [PredefinedFormats.StrictPascalCase]: isStrictPascalCase, [PredefinedFormats.UPPER_CASE]: isUpperCase, - [PredefinedFormats.snake_case]: isSnakeCase, }; export { PredefinedFormatToCheckFunction }; diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/index.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/index.ts index 56297213b66c..11d3953c76fa 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/index.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/index.ts @@ -1,6 +1,6 @@ export { Modifiers } from './enums'; export type { PredefinedFormatsString } from './enums'; -export type { Context, Selector, ValidatorFunction } from './types'; +export { parseOptions } from './parse-options'; export { SCHEMA } from './schema'; export { selectorTypeToMessageString } from './shared'; -export { parseOptions } from './parse-options'; +export type { Context, Selector, ValidatorFunction } from './types'; diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts index e66cdaaebda7..7d37d8d69efb 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/parse-options.ts @@ -1,3 +1,10 @@ +import type { + Context, + NormalizedSelector, + ParsedOptions, + Selector, +} from './types'; + import { getEnumNames } from '../../util'; import { MetaSelectors, @@ -8,12 +15,6 @@ import { UnderscoreOptions, } from './enums'; import { isMetaSelector } from './shared'; -import type { - Context, - NormalizedSelector, - ParsedOptions, - Selector, -} from './types'; import { createValidator } from './validator'; function normalizeOption(option: Selector): NormalizedSelector[] { @@ -32,37 +33,37 @@ function normalizeOption(option: Selector): NormalizedSelector[] { const normalizedOption = { // format options - format: option.format ? option.format.map(f => PredefinedFormats[f]) : null, custom: option.custom ? { - regex: new RegExp(option.custom.regex, 'u'), match: option.custom.match, + regex: new RegExp(option.custom.regex, 'u'), } : null, - leadingUnderscore: - option.leadingUnderscore !== undefined - ? UnderscoreOptions[option.leadingUnderscore] - : null, - trailingUnderscore: - option.trailingUnderscore !== undefined - ? UnderscoreOptions[option.trailingUnderscore] - : null, - prefix: option.prefix && option.prefix.length > 0 ? option.prefix : null, - suffix: option.suffix && option.suffix.length > 0 ? option.suffix : null, - modifiers: option.modifiers?.map(m => Modifiers[m]) ?? null, - types: option.types?.map(m => TypeModifiers[m]) ?? null, filter: option.filter !== undefined ? typeof option.filter === 'string' ? { - regex: new RegExp(option.filter, 'u'), match: true, + regex: new RegExp(option.filter, 'u'), } : { - regex: new RegExp(option.filter.regex, 'u'), match: option.filter.match, + regex: new RegExp(option.filter.regex, 'u'), } : null, + format: option.format ? option.format.map(f => PredefinedFormats[f]) : null, + leadingUnderscore: + option.leadingUnderscore !== undefined + ? UnderscoreOptions[option.leadingUnderscore] + : null, + modifiers: option.modifiers?.map(m => Modifiers[m]) ?? null, + prefix: option.prefix && option.prefix.length > 0 ? option.prefix : null, + suffix: option.suffix && option.suffix.length > 0 ? option.suffix : null, + trailingUnderscore: + option.trailingUnderscore !== undefined + ? UnderscoreOptions[option.trailingUnderscore] + : null, + types: option.types?.map(m => TypeModifiers[m]) ?? null, // calculated ordering weight based on modifiers modifierWeight: weight, }; diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/schema.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/schema.ts index d7016e217f2f..13e4f9a8b3cd 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/schema.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/schema.ts @@ -1,10 +1,11 @@ import type { JSONSchema } from '@typescript-eslint/utils'; -import { getEnumNames } from '../../util'; import type { IndividualAndMetaSelectorsString, ModifiersString, } from './enums'; + +import { getEnumNames } from '../../util'; import { MetaSelectors, Modifiers, @@ -16,51 +17,51 @@ import { const $DEFS: Record = { // enums - underscoreOptions: { - type: 'string', - enum: getEnumNames(UnderscoreOptions), - }, predefinedFormats: { - type: 'string', enum: getEnumNames(PredefinedFormats), + type: 'string', }, typeModifiers: { - type: 'string', enum: getEnumNames(TypeModifiers), + type: 'string', + }, + underscoreOptions: { + enum: getEnumNames(UnderscoreOptions), + type: 'string', }, // repeated types - prefixSuffixConfig: { - type: 'array', - items: { - type: 'string', - minLength: 1, - }, - additionalItems: false, - }, - matchRegexConfig: { - type: 'object', - additionalProperties: false, - properties: { - match: { type: 'boolean' }, - regex: { type: 'string' }, - }, - required: ['match', 'regex'], - }, formatOptionsConfig: { oneOf: [ { - type: 'array', + additionalItems: false, items: { $ref: '#/$defs/predefinedFormats', }, - additionalItems: false, + type: 'array', }, { type: 'null', }, ], }, + matchRegexConfig: { + additionalProperties: false, + properties: { + match: { type: 'boolean' }, + regex: { type: 'string' }, + }, + required: ['match', 'regex'], + type: 'object', + }, + prefixSuffixConfig: { + additionalItems: false, + items: { + minLength: 1, + type: 'string', + }, + type: 'array', + }, }; const UNDERSCORE_SCHEMA: JSONSchema.JSONSchema4 = { @@ -74,17 +75,17 @@ const MATCH_REGEX_SCHEMA: JSONSchema.JSONSchema4 = { }; type JSONSchemaProperties = Record; const FORMAT_OPTIONS_PROPERTIES: JSONSchemaProperties = { + custom: MATCH_REGEX_SCHEMA, + failureMessage: { + type: 'string', + }, format: { $ref: '#/$defs/formatOptionsConfig', }, - custom: MATCH_REGEX_SCHEMA, leadingUnderscore: UNDERSCORE_SCHEMA, - trailingUnderscore: UNDERSCORE_SCHEMA, prefix: PREFIX_SUFFIX_SCHEMA, suffix: PREFIX_SUFFIX_SCHEMA, - failureMessage: { - type: 'string', - }, + trailingUnderscore: UNDERSCORE_SCHEMA, }; function selectorSchema( selectorString: IndividualAndMetaSelectorsString, @@ -95,98 +96,98 @@ function selectorSchema( filter: { oneOf: [ { - type: 'string', minLength: 1, + type: 'string', }, MATCH_REGEX_SCHEMA, ], }, selector: { - type: 'string', enum: [selectorString], + type: 'string', }, }; if (modifiers && modifiers.length > 0) { selector.modifiers = { - type: 'array', + additionalItems: false, items: { - type: 'string', enum: modifiers, + type: 'string', }, - additionalItems: false, + type: 'array', }; } if (allowType) { selector.types = { - type: 'array', + additionalItems: false, items: { $ref: '#/$defs/typeModifiers', }, - additionalItems: false, + type: 'array', }; } return [ { - type: 'object', + additionalProperties: false, description: `Selector '${selectorString}'`, properties: { ...FORMAT_OPTIONS_PROPERTIES, ...selector, }, required: ['selector', 'format'], - additionalProperties: false, + type: 'object', }, ]; } function selectorsSchema(): JSONSchema.JSONSchema4 { return { - type: 'object', + additionalProperties: false, description: 'Multiple selectors in one config', properties: { ...FORMAT_OPTIONS_PROPERTIES, filter: { oneOf: [ { - type: 'string', minLength: 1, + type: 'string', }, MATCH_REGEX_SCHEMA, ], }, - selector: { - type: 'array', + modifiers: { + additionalItems: false, items: { + enum: getEnumNames(Modifiers), type: 'string', - enum: [...getEnumNames(MetaSelectors), ...getEnumNames(Selectors)], }, - additionalItems: false, - }, - modifiers: { type: 'array', + }, + selector: { + additionalItems: false, items: { + enum: [...getEnumNames(MetaSelectors), ...getEnumNames(Selectors)], type: 'string', - enum: getEnumNames(Modifiers), }, - additionalItems: false, + type: 'array', }, types: { - type: 'array', + additionalItems: false, items: { $ref: '#/$defs/typeModifiers', }, - additionalItems: false, + type: 'array', }, }, required: ['selector', 'format'], - additionalProperties: false, + type: 'object', }; } const SCHEMA: JSONSchema.JSONSchema4 = { $defs: $DEFS, - type: 'array', + additionalItems: false, items: { oneOf: [ selectorsSchema(), @@ -326,7 +327,7 @@ const SCHEMA: JSONSchema.JSONSchema4 = { ...selectorSchema('import', false, ['default', 'namespace']), ], }, - additionalItems: false, + type: 'array', }; export { SCHEMA }; diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/shared.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/shared.ts index a7f3e7f6093e..c1c31b78ebe8 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/shared.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/shared.ts @@ -4,6 +4,7 @@ import type { Selectors, SelectorsString, } from './enums'; + import { MetaSelectors } from './enums'; function selectorTypeToMessageString(selectorType: SelectorsString): string { @@ -26,7 +27,7 @@ function isMethodOrPropertySelector( } export { - selectorTypeToMessageString, isMetaSelector, isMethodOrPropertySelector, + selectorTypeToMessageString, }; diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts index fd0910050e00..41df37e8cb15 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts @@ -17,47 +17,47 @@ import type { } from './enums'; interface MatchRegex { - regex: string; match: boolean; + regex: string; } interface Selector { + custom?: MatchRegex; + filter?: MatchRegex | string; // format options format: PredefinedFormatsString[] | null; - custom?: MatchRegex; leadingUnderscore?: UnderscoreOptionsString; - trailingUnderscore?: UnderscoreOptionsString; + modifiers?: ModifiersString[]; prefix?: string[]; - suffix?: string[]; // selector options selector: | IndividualAndMetaSelectorsString | IndividualAndMetaSelectorsString[]; - modifiers?: ModifiersString[]; + suffix?: string[]; + trailingUnderscore?: UnderscoreOptionsString; types?: TypeModifiersString[]; - filter?: MatchRegex | string; } interface NormalizedMatchRegex { - regex: RegExp; match: boolean; + regex: RegExp; } interface NormalizedSelector { + custom: NormalizedMatchRegex | null; + filter: NormalizedMatchRegex | null; // format options format: PredefinedFormats[] | null; - custom: NormalizedMatchRegex | null; leadingUnderscore: UnderscoreOptions | null; - trailingUnderscore: UnderscoreOptions | null; + modifiers: Modifiers[] | null; + // calculated ordering weight based on modifiers + modifierWeight: number; prefix: string[] | null; - suffix: string[] | null; // selector options selector: MetaSelectors | Selectors; - modifiers: Modifiers[] | null; + suffix: string[] | null; + trailingUnderscore: UnderscoreOptions | null; types: TypeModifiers[] | null; - filter: NormalizedMatchRegex | null; - // calculated ordering weight based on modifiers - modifierWeight: number; } type ValidatorFunction = ( diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts index f656769b1b67..afae8bb029b9 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/validator.ts @@ -1,9 +1,12 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type * as ts from 'typescript'; -import { getParserServices } from '../../util'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + import type { SelectorsString } from './enums'; +import type { Context, NormalizedSelector } from './types'; + +import { getParserServices } from '../../util'; import { MetaSelectors, Modifiers, @@ -18,7 +21,6 @@ import { isMethodOrPropertySelector, selectorTypeToMessageString, } from './shared'; -import type { Context, NormalizedSelector } from './types'; function createValidator( type: SelectorsString, @@ -144,29 +146,28 @@ function createValidator( // centralizes the logic for formatting the report data function formatReportData({ affixes, + count, + custom, formats, originalName, - processedName, position, - custom, - count, + processedName, }: { affixes?: string[]; + count?: 'one' | 'two'; + custom?: NonNullable; formats?: PredefinedFormats[]; originalName: string; - processedName?: string; position?: 'leading' | 'prefix' | 'suffix' | 'trailing'; - custom?: NonNullable; - count?: 'one' | 'two'; + processedName?: string; }): Record { return { - type: selectorTypeToMessageString(type), - name: originalName, - processedName, - position, - count, affixes: affixes?.join(', '), + count, formats: formats?.map(f => PredefinedFormats[f]).join(', '), + name: originalName, + position, + processedName, regex: custom?.regex.toString(), regexMatch: custom?.match === true @@ -174,6 +175,7 @@ function createValidator( : custom?.match === false ? 'not match' : null, + type: selectorTypeToMessageString(type), }; } @@ -247,13 +249,13 @@ function createValidator( case UnderscoreOptions.forbid: { if (hasSingleUnderscore()) { context.report({ - node, - messageId: 'unexpectedUnderscore', data: formatReportData({ + count: 'one', originalName, position, - count: 'one', }), + messageId: 'unexpectedUnderscore', + node, }); return null; } @@ -265,13 +267,13 @@ function createValidator( case UnderscoreOptions.require: { if (!hasSingleUnderscore()) { context.report({ - node, - messageId: 'missingUnderscore', data: formatReportData({ + count: 'one', originalName, position, - count: 'one', }), + messageId: 'missingUnderscore', + node, }); return null; } @@ -282,13 +284,13 @@ function createValidator( case UnderscoreOptions.requireDouble: { if (!hasDoubleUnderscore()) { context.report({ - node, - messageId: 'missingUnderscore', data: formatReportData({ + count: 'two', originalName, position, - count: 'two', }), + messageId: 'missingUnderscore', + node, }); return null; } @@ -328,13 +330,13 @@ function createValidator( } context.report({ - node, - messageId: 'missingAffix', data: formatReportData({ + affixes, originalName, position, - affixes, }), + messageId: 'missingAffix', + node, }); return null; } @@ -362,12 +364,12 @@ function createValidator( } context.report({ - node, - messageId: 'satisfyCustom', data: formatReportData({ - originalName, custom, + originalName, }), + messageId: 'satisfyCustom', + node, }); return false; } @@ -397,16 +399,16 @@ function createValidator( } context.report({ - node, - messageId: - originalName === name - ? 'doesNotMatchFormat' - : 'doesNotMatchFormatTrimmed', data: formatReportData({ + formats, originalName, processedName: name, - formats, }), + messageId: + originalName === name + ? 'doesNotMatchFormat' + : 'doesNotMatchFormatTrimmed', + node, }); return false; } diff --git a/packages/eslint-plugin/src/rules/naming-convention.ts b/packages/eslint-plugin/src/rules/naming-convention.ts index 5133126e65be..6363210b12e5 100644 --- a/packages/eslint-plugin/src/rules/naming-convention.ts +++ b/packages/eslint-plugin/src/rules/naming-convention.ts @@ -1,22 +1,24 @@ // This rule was feature-frozen before we enabled no-property-in-node. /* eslint-disable eslint-plugin/no-property-in-node */ -import { PatternVisitor } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, TSESLint } from '@typescript-eslint/utils'; import type { ScriptTarget } from 'typescript'; -import { - collectVariables, - createRule, - getParserServices, - requiresQuoting as _requiresQuoting, -} from '../util'; +import { PatternVisitor } from '@typescript-eslint/scope-manager'; +import { AST_NODE_TYPES, TSESLint } from '@typescript-eslint/utils'; + import type { Context, Selector, ValidatorFunction, } from './naming-convention-utils'; + +import { + requiresQuoting as _requiresQuoting, + collectVariables, + createRule, + getParserServices, +} from '../util'; import { Modifiers, parseOptions, SCHEMA } from './naming-convention-utils'; type MessageIds = @@ -36,53 +38,53 @@ type Options = Selector[]; // note that that rule ignores leading and trailing underscores and only checks those in the middle of a variable name const defaultCamelCaseAllTheThingsConfig: Options = [ { - selector: 'default', format: ['camelCase'], leadingUnderscore: 'allow', + selector: 'default', trailingUnderscore: 'allow', }, { - selector: 'import', format: ['camelCase', 'PascalCase'], + selector: 'import', }, { - selector: 'variable', format: ['camelCase', 'UPPER_CASE'], leadingUnderscore: 'allow', + selector: 'variable', trailingUnderscore: 'allow', }, { - selector: 'typeLike', format: ['PascalCase'], + selector: 'typeLike', }, ]; export default createRule({ name: 'naming-convention', meta: { + type: 'suggestion', docs: { description: 'Enforce naming conventions for everything across a codebase', // technically only requires type checking if the user uses "type" modifiers requiresTypeChecking: true, }, - type: 'suggestion', messages: { - unexpectedUnderscore: - '{{type}} name `{{name}}` must not have a {{position}} underscore.', - missingUnderscore: - '{{type}} name `{{name}}` must have {{count}} {{position}} underscore(s).', - missingAffix: - '{{type}} name `{{name}}` must have one of the following {{position}}es: {{affixes}}', - satisfyCustom: - '{{type}} name `{{name}}` must {{regexMatch}} the RegExp: {{regex}}', doesNotMatchFormat: '{{type}} name `{{name}}` must match one of the following formats: {{formats}}', doesNotMatchFormatTrimmed: '{{type}} name `{{name}}` trimmed as `{{processedName}}` must match one of the following formats: {{formats}}', + missingAffix: + '{{type}} name `{{name}}` must have one of the following {{position}}es: {{affixes}}', + missingUnderscore: + '{{type}} name `{{name}}` must have {{count}} {{position}} underscore(s).', + satisfyCustom: + '{{type}} name `{{name}}` must {{regexMatch}} the RegExp: {{regex}}', + unexpectedUnderscore: + '{{type}} name `{{name}}` must not have a {{position}} underscore.', }, schema: SCHEMA, }, @@ -106,14 +108,14 @@ export default createRule({ function handleMember( validator: ValidatorFunction, node: + | TSESTree.AccessorPropertyNonComputedName | TSESTree.MethodDefinitionNonComputedName | TSESTree.PropertyDefinitionNonComputedName | TSESTree.PropertyNonComputedName | TSESTree.TSAbstractMethodDefinitionNonComputedName | TSESTree.TSAbstractPropertyDefinitionNonComputedName | TSESTree.TSMethodSignatureNonComputedName - | TSESTree.TSPropertySignatureNonComputedName - | TSESTree.AccessorPropertyNonComputedName, + | TSESTree.TSPropertySignatureNonComputedName, modifiers: Set, ): void { const key = node.key; @@ -126,13 +128,13 @@ export default createRule({ function getMemberModifiers( node: + | TSESTree.AccessorProperty | TSESTree.MethodDefinition | TSESTree.PropertyDefinition + | TSESTree.TSAbstractAccessorProperty | TSESTree.TSAbstractMethodDefinition | TSESTree.TSAbstractPropertyDefinition - | TSESTree.TSParameterProperty - | TSESTree.AccessorProperty - | TSESTree.TSAbstractAccessorProperty, + | TSESTree.TSParameterProperty, ): Set { const modifiers = new Set(); if ('key' in node && node.key.type === AST_NODE_TYPES.PrivateIdentifier) { @@ -225,17 +227,57 @@ export default createRule({ const selectors: { readonly [k in keyof TSESLint.RuleListener]: Readonly<{ - validator: ValidatorFunction; handler: ( node: Parameters>[0], validator: ValidatorFunction, ) => void; + validator: ValidatorFunction; }>; } = { // #region import + 'FunctionDeclaration, TSDeclareFunction, FunctionExpression': { + handler: ( + node: + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.TSDeclareFunction, + validator, + ): void => { + if (node.id == null) { + return; + } + + const modifiers = new Set(); + // functions create their own nested scope + const scope = context.sourceCode.getScope(node).upper; + + if (isGlobal(scope)) { + modifiers.add(Modifiers.global); + } + + if (isExported(node, node.id.name, scope)) { + modifiers.add(Modifiers.exported); + } + + if (isUnused(node.id.name, scope)) { + modifiers.add(Modifiers.unused); + } + + if (node.async) { + modifiers.add(Modifiers.async); + } + + validator(node.id, modifiers); + }, + validator: validators.function, + }, + + // #endregion + + // #region variable + 'ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier': { - validator: validators.import, handler: ( node: | TSESTree.ImportDefaultSpecifier @@ -263,14 +305,14 @@ export default createRule({ validator(node.local, modifiers); }, + validator: validators.import, }, // #endregion - // #region variable + // #region function VariableDeclarator: { - validator: validators.variable, handler: (node, validator): void => { const identifiers = getIdentifiersFromPattern(node.id); @@ -307,112 +349,32 @@ export default createRule({ validator(id, modifiers); }); }, - }, - - // #endregion - - // #region function - - 'FunctionDeclaration, TSDeclareFunction, FunctionExpression': { - validator: validators.function, - handler: ( - node: - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.TSDeclareFunction, - validator, - ): void => { - if (node.id == null) { - return; - } - - const modifiers = new Set(); - // functions create their own nested scope - const scope = context.sourceCode.getScope(node).upper; - - if (isGlobal(scope)) { - modifiers.add(Modifiers.global); - } - - if (isExported(node, node.id.name, scope)) { - modifiers.add(Modifiers.exported); - } - - if (isUnused(node.id.name, scope)) { - modifiers.add(Modifiers.unused); - } - - if (node.async) { - modifiers.add(Modifiers.async); - } - - validator(node.id, modifiers); - }, + validator: validators.variable, }, // #endregion function // #region parameter - 'FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression': + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]': { - validator: validators.parameter, handler: ( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.TSDeclareFunction - | TSESTree.TSEmptyBodyFunctionExpression, + | TSESTree.PropertyDefinitionNonComputedName + | TSESTree.TSAbstractPropertyDefinitionNonComputedName, validator, ): void => { - node.params.forEach(param => { - if (param.type === AST_NODE_TYPES.TSParameterProperty) { - return; - } - - const identifiers = getIdentifiersFromPattern(param); - - identifiers.forEach(i => { - const modifiers = new Set(); - - if (isDestructured(i)) { - modifiers.add(Modifiers.destructured); - } - - if (isUnused(i.name, context.sourceCode.getScope(i))) { - modifiers.add(Modifiers.unused); - } - - validator(i, modifiers); - }); - }); + const modifiers = getMemberModifiers(node); + handleMember(validator, node, modifiers); }, + validator: validators.classProperty, }, // #endregion parameter // #region parameterProperty - TSParameterProperty: { - validator: validators.parameterProperty, - handler: (node, validator): void => { - const modifiers = getMemberModifiers(node); - - const identifiers = getIdentifiersFromPattern(node.parameter); - - identifiers.forEach(i => { - validator(i, modifiers); - }); - }, - }, - - // #endregion parameterProperty - - // #region property - ':not(ObjectPattern) > Property[computed = false][kind = "init"][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]': { - validator: validators.objectLiteralProperty, handler: ( node: TSESTree.PropertyNonComputedName, validator, @@ -420,55 +382,28 @@ export default createRule({ const modifiers = new Set([Modifiers.public]); handleMember(validator, node, modifiers); }, + validator: validators.objectLiteralProperty, }, - ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]': - { - validator: validators.classProperty, - handler: ( - node: - | TSESTree.PropertyDefinitionNonComputedName - | TSESTree.TSAbstractPropertyDefinitionNonComputedName, - validator, - ): void => { - const modifiers = getMemberModifiers(node); - handleMember(validator, node, modifiers); - }, - }, - - 'TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type != "TSFunctionType"]': - { - validator: validators.typeProperty, - handler: ( - node: TSESTree.TSPropertySignatureNonComputedName, - validator, - ): void => { - const modifiers = new Set([Modifiers.public]); - if (node.readonly) { - modifiers.add(Modifiers.readonly); - } - - handleMember(validator, node, modifiers); - }, - }, - - // #endregion property + // #endregion parameterProperty - // #region method + // #region property [[ - 'Property[computed = false][kind = "init"][value.type = "ArrowFunctionExpression"]', - 'Property[computed = false][kind = "init"][value.type = "FunctionExpression"]', - 'Property[computed = false][kind = "init"][value.type = "TSEmptyBodyFunctionExpression"]', + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "ArrowFunctionExpression"]', + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "FunctionExpression"]', + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "TSEmptyBodyFunctionExpression"]', + ':matches(MethodDefinition, TSAbstractMethodDefinition)[computed = false][kind = "method"]', ].join(', ')]: { - validator: validators.objectLiteralMethod, handler: ( node: - | TSESTree.PropertyNonComputedName - | TSESTree.TSMethodSignatureNonComputedName, + | TSESTree.MethodDefinitionNonComputedName + | TSESTree.PropertyDefinitionNonComputedName + | TSESTree.TSAbstractMethodDefinitionNonComputedName + | TSESTree.TSAbstractPropertyDefinitionNonComputedName, validator, ): void => { - const modifiers = new Set([Modifiers.public]); + const modifiers = getMemberModifiers(node); if (isAsyncMemberOrProperty(node)) { modifiers.add(Modifiers.async); @@ -476,24 +411,35 @@ export default createRule({ handleMember(validator, node, modifiers); }, + validator: validators.classMethod, }, [[ - ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "ArrowFunctionExpression"]', - ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "FunctionExpression"]', - ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "TSEmptyBodyFunctionExpression"]', - ':matches(MethodDefinition, TSAbstractMethodDefinition)[computed = false][kind = "method"]', + 'MethodDefinition[computed = false]:matches([kind = "get"], [kind = "set"])', + 'TSAbstractMethodDefinition[computed = false]:matches([kind="get"], [kind="set"])', ].join(', ')]: { - validator: validators.classMethod, handler: ( - node: - | TSESTree.MethodDefinitionNonComputedName - | TSESTree.PropertyDefinitionNonComputedName - | TSESTree.TSAbstractMethodDefinitionNonComputedName - | TSESTree.TSAbstractPropertyDefinitionNonComputedName, + node: TSESTree.MethodDefinitionNonComputedName, validator, ): void => { const modifiers = getMemberModifiers(node); + handleMember(validator, node, modifiers); + }, + validator: validators.classicAccessor, + }, + + [[ + 'Property[computed = false][kind = "init"][value.type = "ArrowFunctionExpression"]', + 'Property[computed = false][kind = "init"][value.type = "FunctionExpression"]', + 'Property[computed = false][kind = "init"][value.type = "TSEmptyBodyFunctionExpression"]', + ].join(', ')]: { + handler: ( + node: + | TSESTree.PropertyNonComputedName + | TSESTree.TSMethodSignatureNonComputedName, + validator, + ): void => { + const modifiers = new Set([Modifiers.public]); if (isAsyncMemberOrProperty(node)) { modifiers.add(Modifiers.async); @@ -501,13 +447,17 @@ export default createRule({ handleMember(validator, node, modifiers); }, + validator: validators.objectLiteralMethod, }, + // #endregion property + + // #region method + [[ 'TSMethodSignature[computed = false]', 'TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type = "TSFunctionType"]', ].join(', ')]: { - validator: validators.typeMethod, handler: ( node: | TSESTree.TSMethodSignatureNonComputedName @@ -517,80 +467,110 @@ export default createRule({ const modifiers = new Set([Modifiers.public]); handleMember(validator, node, modifiers); }, + validator: validators.typeMethod, }, + [[ + AST_NODE_TYPES.AccessorProperty, + AST_NODE_TYPES.TSAbstractAccessorProperty, + ].join(', ')]: { + handler: ( + node: TSESTree.AccessorPropertyNonComputedName, + validator, + ): void => { + const modifiers = getMemberModifiers(node); + handleMember(validator, node, modifiers); + }, + validator: validators.autoAccessor, + }, + + 'FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression': + { + handler: ( + node: + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.TSDeclareFunction + | TSESTree.TSEmptyBodyFunctionExpression, + validator, + ): void => { + node.params.forEach(param => { + if (param.type === AST_NODE_TYPES.TSParameterProperty) { + return; + } + + const identifiers = getIdentifiersFromPattern(param); + + identifiers.forEach(i => { + const modifiers = new Set(); + + if (isDestructured(i)) { + modifiers.add(Modifiers.destructured); + } + + if (isUnused(i.name, context.sourceCode.getScope(i))) { + modifiers.add(Modifiers.unused); + } + + validator(i, modifiers); + }); + }); + }, + validator: validators.parameter, + }, + // #endregion method // #region accessor 'Property[computed = false]:matches([kind = "get"], [kind = "set"])': { - validator: validators.classicAccessor, handler: (node: TSESTree.PropertyNonComputedName, validator): void => { const modifiers = new Set([Modifiers.public]); handleMember(validator, node, modifiers); }, + validator: validators.classicAccessor, }, - [[ - 'MethodDefinition[computed = false]:matches([kind = "get"], [kind = "set"])', - 'TSAbstractMethodDefinition[computed = false]:matches([kind="get"], [kind="set"])', - ].join(', ')]: { - validator: validators.classicAccessor, - handler: ( - node: TSESTree.MethodDefinitionNonComputedName, - validator, - ): void => { + TSParameterProperty: { + handler: (node, validator): void => { const modifiers = getMemberModifiers(node); - handleMember(validator, node, modifiers); + + const identifiers = getIdentifiersFromPattern(node.parameter); + + identifiers.forEach(i => { + validator(i, modifiers); + }); }, + validator: validators.parameterProperty, }, // #endregion accessor // #region autoAccessor - [[ - AST_NODE_TYPES.AccessorProperty, - AST_NODE_TYPES.TSAbstractAccessorProperty, - ].join(', ')]: { - validator: validators.autoAccessor, - handler: ( - node: TSESTree.AccessorPropertyNonComputedName, - validator, - ): void => { - const modifiers = getMemberModifiers(node); - handleMember(validator, node, modifiers); + 'TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type != "TSFunctionType"]': + { + handler: ( + node: TSESTree.TSPropertySignatureNonComputedName, + validator, + ): void => { + const modifiers = new Set([Modifiers.public]); + if (node.readonly) { + modifiers.add(Modifiers.readonly); + } + + handleMember(validator, node, modifiers); + }, + validator: validators.typeProperty, }, - }, // #endregion autoAccessor // #region enumMember // computed is optional, so can't do [computed = false] - 'TSEnumMember[computed != true]': { - validator: validators.enumMember, - handler: ( - node: TSESTree.TSEnumMemberNonComputedName, - validator, - ): void => { - const id = node.id; - const modifiers = new Set(); - - if (requiresQuoting(id, compilerOptions.target)) { - modifiers.add(Modifiers.requiresQuotes); - } - - validator(id, modifiers); - }, - }, - - // #endregion enumMember - - // #region class - 'ClassDeclaration, ClassExpression': { - validator: validators.class, handler: ( node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, validator, @@ -618,17 +598,18 @@ export default createRule({ validator(id, modifiers); }, + validator: validators.class, }, - // #endregion class + // #endregion enumMember - // #region interface + // #region class - TSInterfaceDeclaration: { - validator: validators.interface, + TSEnumDeclaration: { handler: (node, validator): void => { const modifiers = new Set(); - const scope = context.sourceCode.getScope(node); + // enums create their own nested scope + const scope = context.sourceCode.getScope(node).upper; if (isExported(node, node.id.name, scope)) { modifiers.add(Modifiers.exported); @@ -640,14 +621,35 @@ export default createRule({ validator(node.id, modifiers); }, + validator: validators.enum, + }, + + // #endregion class + + // #region interface + + 'TSEnumMember[computed != true]': { + handler: ( + node: TSESTree.TSEnumMemberNonComputedName, + validator, + ): void => { + const id = node.id; + const modifiers = new Set(); + + if (requiresQuoting(id, compilerOptions.target)) { + modifiers.add(Modifiers.requiresQuotes); + } + + validator(id, modifiers); + }, + validator: validators.enumMember, }, // #endregion interface // #region typeAlias - TSTypeAliasDeclaration: { - validator: validators.typeAlias, + TSInterfaceDeclaration: { handler: (node, validator): void => { const modifiers = new Set(); const scope = context.sourceCode.getScope(node); @@ -662,18 +664,17 @@ export default createRule({ validator(node.id, modifiers); }, + validator: validators.interface, }, // #endregion typeAlias // #region enum - TSEnumDeclaration: { - validator: validators.enum, + TSTypeAliasDeclaration: { handler: (node, validator): void => { const modifiers = new Set(); - // enums create their own nested scope - const scope = context.sourceCode.getScope(node).upper; + const scope = context.sourceCode.getScope(node); if (isExported(node, node.id.name, scope)) { modifiers.add(Modifiers.exported); @@ -685,6 +686,7 @@ export default createRule({ validator(node.id, modifiers); }, + validator: validators.typeAlias, }, // #endregion enum @@ -692,7 +694,6 @@ export default createRule({ // #region typeParameter 'TSTypeParameterDeclaration > TSTypeParameter': { - validator: validators.typeParameter, handler: (node: TSESTree.TSTypeParameter, validator): void => { const modifiers = new Set(); const scope = context.sourceCode.getScope(node); @@ -703,13 +704,14 @@ export default createRule({ validator(node.name, modifiers); }, + validator: validators.typeParameter, }, // #endregion typeParameter }; return Object.fromEntries( - Object.entries(selectors).map(([selector, { validator, handler }]) => { + Object.entries(selectors).map(([selector, { handler, validator }]) => { return [ selector, (node: Parameters[0]): void => { diff --git a/packages/eslint-plugin/src/rules/no-array-constructor.ts b/packages/eslint-plugin/src/rules/no-array-constructor.ts index b164e5713e6d..a315789334e4 100644 --- a/packages/eslint-plugin/src/rules/no-array-constructor.ts +++ b/packages/eslint-plugin/src/rules/no-array-constructor.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isOptionalCallExpression } from '../util'; @@ -9,8 +10,8 @@ export default createRule({ type: 'suggestion', docs: { description: 'Disallow generic `Array` constructors', - recommended: 'recommended', extendsBaseRule: true, + recommended: 'recommended', }, fixable: 'code', messages: { diff --git a/packages/eslint-plugin/src/rules/no-array-delete.ts b/packages/eslint-plugin/src/rules/no-array-delete.ts index 9b6230ebf821..a179418a7b01 100644 --- a/packages/eslint-plugin/src/rules/no-array-delete.ts +++ b/packages/eslint-plugin/src/rules/no-array-delete.ts @@ -1,7 +1,8 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import type * as ts from 'typescript'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; + import { createRule, getConstrainedTypeAtLocation, @@ -13,13 +14,13 @@ 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: 'recommended', requiresTypeChecking: true, }, + hasSuggestions: true, messages: { noArrayDelete: 'Using the `delete` operator with an array expression is unsafe.', diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index d4620aa7c031..df8fa6bb13cf 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -20,6 +21,7 @@ type MessageIds = 'baseToString'; export default createRule({ name: 'no-base-to-string', meta: { + type: 'suggestion', docs: { description: 'Require `.toString()` to only be called on objects which provide useful information when stringified', @@ -33,20 +35,19 @@ export default createRule({ schema: [ { type: 'object', + additionalProperties: false, properties: { ignoredTypeNames: { + type: 'array', description: 'Stringified regular expressions of type names to ignore.', - type: 'array', items: { type: 'string', }, }, }, - additionalProperties: false, }, ], - type: 'suggestion', }, defaultOptions: [ { @@ -71,12 +72,12 @@ export default createRule({ } context.report({ + node, + messageId: 'baseToString', data: { - certainty, name: context.sourceCode.getText(node), + certainty, }, - messageId: 'baseToString', - node, }); } diff --git a/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts index 160827fc5a32..101f3514835b 100644 --- a/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-confusing-non-null-assertion.ts @@ -1,18 +1,19 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import type { ReportDescriptor, RuleFix, } from '@typescript-eslint/utils/ts-eslint'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; + import { createRule } from '../util'; type MessageId = - | 'confusingEqual' | 'confusingAssign' + | 'confusingEqual' | 'confusingOperator' - | 'notNeedInEqualTest' | 'notNeedInAssign' + | 'notNeedInEqualTest' | 'notNeedInOperator' | 'wrapUpLeft'; @@ -41,17 +42,17 @@ export default createRule<[], MessageId>({ }, hasSuggestions: true, messages: { - confusingEqual: - 'Confusing combination of non-null assertion and equality test like `a! == b`, which looks very similar to `a !== b`.', confusingAssign: 'Confusing combination of non-null assertion and assignment like `a! = b`, which looks very similar to `a != b`.', + confusingEqual: + 'Confusing combination of non-null assertion and equality test like `a! == b`, which looks very similar to `a !== b`.', confusingOperator: 'Confusing combination of non-null assertion and `{{operator}}` operator like `a! {{operator}} b`, which might be misinterpreted as `!(a {{operator}} b)`.', - notNeedInEqualTest: - 'Remove unnecessary non-null assertion (!) in equality test.', notNeedInAssign: 'Remove unnecessary non-null assertion (!) in assignment left-hand side.', + notNeedInEqualTest: + 'Remove unnecessary non-null assertion (!) in equality test.', notNeedInOperator: 'Remove possibly unnecessary non-null assertion (!) in the left operand of the `{{operator}}` operator.', @@ -65,7 +66,7 @@ export default createRule<[], MessageId>({ create(context) { function confusingOperatorToMessageData( operator: ConfusingOperator, - ): Pick, 'messageId' | 'data'> { + ): Pick, 'data' | 'messageId'> { switch (operator) { case '=': return { diff --git a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts index ff66138dcf9d..22b487b89bbf 100644 --- a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts +++ b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts @@ -1,9 +1,11 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; import type { MakeRequired } from '../util'; + import { createRule, getConstrainedTypeAtLocation, @@ -35,20 +37,19 @@ export type MessageId = export default createRule({ name: 'no-confusing-void-expression', meta: { + type: 'problem', docs: { description: 'Require expressions of type void to appear in statement position', recommended: 'strict', requiresTypeChecking: true, }, + fixable: 'code', + hasSuggestions: true, messages: { invalidVoidExpr: 'Placing a void expression inside another expression is forbidden. ' + 'Move it to its own statement instead.', - invalidVoidExprWrapVoid: - 'Void expressions used inside another expression ' + - 'must be moved to its own statement ' + - 'or marked explicitly with the `void` operator.', invalidVoidExprArrow: 'Returning a void expression from an arrow function shorthand is forbidden. ' + 'Please add braces to the arrow function.', @@ -64,29 +65,30 @@ export default createRule({ invalidVoidExprReturnWrapVoid: 'Void expressions returned from a function ' + 'must be marked explicitly with the `void` operator.', + invalidVoidExprWrapVoid: + 'Void expressions used inside another expression ' + + 'must be moved to its own statement ' + + 'or marked explicitly with the `void` operator.', voidExprWrapVoid: 'Mark with an explicit `void` operator.', }, schema: [ { type: 'object', + additionalProperties: false, properties: { ignoreArrowShorthand: { + type: 'boolean', description: 'Whether to ignore "shorthand" `() =>` arrow functions: those without `{ ... }` braces.', - type: 'boolean', }, ignoreVoidOperator: { + type: 'boolean', description: 'Whether to ignore returns that start with the `void` operator.', - type: 'boolean', }, }, - additionalProperties: false, }, ], - type: 'problem', - fixable: 'code', - hasSuggestions: true, }, defaultOptions: [{ ignoreArrowShorthand: false, ignoreVoidOperator: false }], @@ -341,9 +343,9 @@ export default createRule({ ); if ( ![ + AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionDeclaration, AST_NODE_TYPES.FunctionExpression, - AST_NODE_TYPES.ArrowFunctionExpression, ].includes(blockParent.type) ) { // e.g. `if (cond) { return; }` diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 8eea3c8a1d35..d8e7ebc0105e 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -10,6 +11,7 @@ type IdentifierLike = TSESTree.Identifier | TSESTree.JSXIdentifier; export default createRule({ name: 'no-deprecated', meta: { + type: 'problem', docs: { description: 'Disallow using code marked as `@deprecated`', recommended: 'strict', @@ -20,7 +22,6 @@ export default createRule({ deprecatedWithReason: `\`{{name}}\` is deprecated. {{reason}}`, }, schema: [], - type: 'problem', }, defaultOptions: [], create(context) { @@ -301,12 +302,12 @@ export default createRule({ context.report({ ...(reason ? { - data: { name: node.name, reason }, messageId: 'deprecatedWithReason', + data: { name: node.name, reason }, } : { - data: { name: node.name }, messageId: 'deprecated', + data: { name: node.name }, }), node, }); diff --git a/packages/eslint-plugin/src/rules/no-dupe-class-members.ts b/packages/eslint-plugin/src/rules/no-dupe-class-members.ts index 08dd0b35d3b8..60f51d317ae8 100644 --- a/packages/eslint-plugin/src/rules/no-dupe-class-members.ts +++ b/packages/eslint-plugin/src/rules/no-dupe-class-members.ts @@ -1,10 +1,12 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -22,8 +24,8 @@ export default createRule({ extendsBaseRule: true, }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, messages: baseRule.meta.messages, + schema: baseRule.meta.schema, }, defaultOptions: [], create(context) { diff --git a/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts b/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts index d8ac6586666d..41464609c518 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; diff --git a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts index c1123fe4e71a..c6b700e27ff7 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-type-constituents.ts @@ -1,7 +1,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; +import type { Type } from 'typescript'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; -import type { Type } from 'typescript'; import * as ts from 'typescript'; import { @@ -21,7 +22,7 @@ export type Options = [ export type MessageIds = 'duplicate' | 'unnecessary'; -const astIgnoreKeys = new Set(['range', 'loc', 'parent']); +const astIgnoreKeys = new Set(['loc', 'parent', 'range']); const isSameAstNode = (actualNode: unknown, expectedNode: unknown): boolean => { if (actualNode === expectedNode) { @@ -91,16 +92,16 @@ export default createRule({ }, schema: [ { - additionalProperties: false, type: 'object', + additionalProperties: false, properties: { ignoreIntersections: { - description: 'Whether to ignore `&` intersections.', type: 'boolean', + description: 'Whether to ignore `&` intersections.', }, ignoreUnions: { - description: 'Whether to ignore `|` unions.', type: 'boolean', + description: 'Whether to ignore `|` unions.', }, }, }, @@ -137,11 +138,11 @@ export default createRule({ data?: Record, ): void => { const getUnionOrIntersectionToken = ( - where: 'Before' | 'After', + where: 'After' | 'Before', at: number, ): TSESTree.Token | undefined => sourceCode[`getTokens${where}`](constituentNode, { - filter: token => ['|', '&'].includes(token.value), + filter: token => ['&', '|'].includes(token.value), }).at(at); const beforeUnionOrIntersectionToken = getUnionOrIntersectionToken( @@ -179,13 +180,13 @@ export default createRule({ ); } context.report({ - data, - messageId, - node: constituentNode, loc: { start: constituentNode.loc.start, end: (bracketAfterTokens.at(-1) ?? constituentNode).loc.end, }, + node: constituentNode, + messageId, + data, fix: fixer => [ beforeUnionOrIntersectionToken, diff --git a/packages/eslint-plugin/src/rules/no-dynamic-delete.ts b/packages/eslint-plugin/src/rules/no-dynamic-delete.ts index fb487fa6b7fc..aa009578636d 100644 --- a/packages/eslint-plugin/src/rules/no-dynamic-delete.ts +++ b/packages/eslint-plugin/src/rules/no-dynamic-delete.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; @@ -6,6 +7,7 @@ import { createRule, nullThrows, NullThrowsReasons } from '../util'; export default createRule({ name: 'no-dynamic-delete', meta: { + type: 'suggestion', docs: { description: 'Disallow using the `delete` operator on computed key expressions', @@ -16,7 +18,6 @@ export default createRule({ dynamicDelete: 'Do not delete dynamically computed property keys.', }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { @@ -47,9 +48,9 @@ export default createRule({ } context.report({ - fix: createFixer(node.argument), - messageId: 'dynamicDelete', node: node.argument.property, + messageId: 'dynamicDelete', + fix: createFixer(node.argument), }); }, }; @@ -80,7 +81,7 @@ export default createRule({ function isAcceptableIndexExpression(property: TSESTree.Expression): boolean { return ( (property.type === AST_NODE_TYPES.Literal && - ['string', 'number'].includes(typeof property.value)) || + ['number', 'string'].includes(typeof property.value)) || (property.type === AST_NODE_TYPES.UnaryExpression && property.operator === '-' && property.argument.type === AST_NODE_TYPES.Literal && diff --git a/packages/eslint-plugin/src/rules/no-empty-function.ts b/packages/eslint-plugin/src/rules/no-empty-function.ts index 1b5061dc2d36..ba5371fba3ef 100644 --- a/packages/eslint-plugin/src/rules/no-empty-function.ts +++ b/packages/eslint-plugin/src/rules/no-empty-function.ts @@ -1,11 +1,13 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule, deepMerge } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -54,12 +56,12 @@ export default createRule({ type: 'suggestion', docs: { description: 'Disallow empty functions', - recommended: 'stylistic', extendsBaseRule: true, + recommended: 'stylistic', }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: [schema], messages: baseRule.meta.messages, + schema: [schema], }, defaultOptions: [ { diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index c776eb6d6eb8..855d228dbd34 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -1,5 +1,6 @@ -import { ScopeType } from '@typescript-eslint/scope-manager'; import type { TSESLint } from '@typescript-eslint/utils'; + +import { ScopeType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isDefinitionFile } from '../util'; @@ -15,11 +16,10 @@ export default createRule({ name: 'no-empty-interface', meta: { type: 'suggestion', + deprecated: true, docs: { description: 'Disallow the declaration of empty interfaces', }, - deprecated: true, - replacedBy: ['@typescript-eslint/no-empty-object-type'], fixable: 'code', hasSuggestions: true, messages: { @@ -27,15 +27,16 @@ export default createRule({ noEmptyWithSuper: 'An interface declaring no members is equivalent to its supertype.', }, + replacedBy: ['@typescript-eslint/no-empty-object-type'], schema: [ { type: 'object', additionalProperties: false, properties: { allowSingleExtends: { + type: 'boolean', description: 'Whether to allow empty interfaces that extend a single other interface.', - type: 'boolean', }, }, }, diff --git a/packages/eslint-plugin/src/rules/no-empty-object-type.ts b/packages/eslint-plugin/src/rules/no-empty-object-type.ts index cf41ee2031cd..0e5815d4e19e 100644 --- a/packages/eslint-plugin/src/rules/no-empty-object-type.ts +++ b/packages/eslint-plugin/src/rules/no-empty-object-type.ts @@ -1,4 +1,5 @@ import type { TSESLint } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -17,8 +18,8 @@ export type Options = [ export type MessageIds = | 'noEmptyInterface' - | 'noEmptyObject' | 'noEmptyInterfaceWithSuper' + | 'noEmptyObject' | 'replaceEmptyInterface' | 'replaceEmptyInterfaceWithSuper' | 'replaceEmptyObjectType'; @@ -42,9 +43,9 @@ export default createRule({ hasSuggestions: true, messages: { noEmptyInterface: noEmptyMessage('An empty interface declaration'), - noEmptyObject: noEmptyMessage('The `{}` ("empty object") type'), noEmptyInterfaceWithSuper: 'An interface declaring no members is equivalent to its supertype.', + noEmptyObject: noEmptyMessage('The `{}` ("empty object") type'), replaceEmptyInterface: 'Replace empty interface with `{{replacement}}`.', replaceEmptyInterfaceWithSuper: 'Replace empty interface with a type alias.', @@ -56,19 +57,19 @@ export default createRule({ additionalProperties: false, properties: { allowInterfaces: { + type: 'string', description: 'Whether to allow empty interfaces.', enum: ['always', 'never', 'with-single-extends'], - type: 'string', }, allowObjectTypes: { + type: 'string', description: 'Whether to allow empty object type literals.', enum: ['always', 'never'], - type: 'string', }, allowWithName: { + type: 'string', description: 'A stringified regular expression to allow interfaces and object type aliases with the configured name.', - type: 'string', }, }, }, @@ -80,7 +81,7 @@ export default createRule({ allowObjectTypes: 'never', }, ], - create(context, [{ allowInterfaces, allowWithName, allowObjectTypes }]) { + create(context, [{ allowInterfaces, allowObjectTypes, allowWithName }]) { const allowWithNameTester = allowWithName ? new RegExp(allowWithName, 'u') : undefined; @@ -112,11 +113,12 @@ export default createRule({ if (extend.length === 0) { context.report({ - data: { option: 'allowInterfaces' }, node: node.id, messageId: 'noEmptyInterface', + data: { option: 'allowInterfaces' }, ...(!mergedWithClassDeclaration && { suggest: ['object', 'unknown'].map(replacement => ({ + messageId: 'replaceEmptyInterface', data: { replacement }, fix(fixer): TSESLint.RuleFix { const id = context.sourceCode.getText(node.id); @@ -129,7 +131,6 @@ export default createRule({ `type ${id}${typeParam} = ${replacement}`, ); }, - messageId: 'replaceEmptyInterface', })), }), }); @@ -142,6 +143,7 @@ export default createRule({ ...(!mergedWithClassDeclaration && { suggest: [ { + messageId: 'replaceEmptyInterfaceWithSuper', fix(fixer): TSESLint.RuleFix { const extended = context.sourceCode.getText(extend[0]); const id = context.sourceCode.getText(node.id); @@ -154,7 +156,6 @@ export default createRule({ `type ${id}${typeParam} = ${extended}`, ); }, - messageId: 'replaceEmptyInterfaceWithSuper', }, ], }), @@ -174,12 +175,12 @@ export default createRule({ } context.report({ - data: { option: 'allowObjectTypes' }, - messageId: 'noEmptyObject', node, + messageId: 'noEmptyObject', + data: { option: 'allowObjectTypes' }, suggest: ['object', 'unknown'].map(replacement => ({ - data: { replacement }, messageId: 'replaceEmptyObjectType', + data: { replacement }, fix: (fixer): TSESLint.RuleFix => fixer.replaceText(node, replacement), })), diff --git a/packages/eslint-plugin/src/rules/no-explicit-any.ts b/packages/eslint-plugin/src/rules/no-explicit-any.ts index 923750847365..1571617d8242 100644 --- a/packages/eslint-plugin/src/rules/no-explicit-any.ts +++ b/packages/eslint-plugin/src/rules/no-explicit-any.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -22,11 +23,11 @@ export default createRule({ fixable: 'code', hasSuggestions: true, messages: { - unexpectedAny: 'Unexpected any. Specify a different type.', - suggestUnknown: - 'Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.', suggestNever: "Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of.", + suggestUnknown: + 'Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.', + unexpectedAny: 'Unexpected any. Specify a different type.', }, schema: [ { @@ -34,13 +35,13 @@ export default createRule({ additionalProperties: false, properties: { fixToUnknown: { + type: 'boolean', description: 'Whether to enable auto-fixing in which the `any` type is converted to the `unknown` type.', - type: 'boolean', }, ignoreRestArgs: { - description: 'Whether to ignore rest parameter arrays.', type: 'boolean', + description: 'Whether to ignore rest parameter arrays.', }, }, }, @@ -52,7 +53,7 @@ export default createRule({ ignoreRestArgs: false, }, ], - create(context, [{ ignoreRestArgs, fixToUnknown }]) { + create(context, [{ fixToUnknown, ignoreRestArgs }]) { /** * Checks if the node is an arrow function, function/constructor declaration or function expression * @param node the node to be validated. @@ -64,13 +65,13 @@ export default createRule({ AST_NODE_TYPES.ArrowFunctionExpression, // const x = (...args: any[]) => {}; AST_NODE_TYPES.FunctionDeclaration, // function f(...args: any[]) {} AST_NODE_TYPES.FunctionExpression, // const x = function(...args: any[]) {}; - AST_NODE_TYPES.TSEmptyBodyFunctionExpression, // declare class A { f(...args: any[]): unknown; } - AST_NODE_TYPES.TSFunctionType, // type T = (...args: any[]) => unknown; - AST_NODE_TYPES.TSConstructorType, // type T = new (...args: any[]) => unknown AST_NODE_TYPES.TSCallSignatureDeclaration, // type T = {(...args: any[]): unknown}; + AST_NODE_TYPES.TSConstructorType, // type T = new (...args: any[]) => unknown AST_NODE_TYPES.TSConstructSignatureDeclaration, // type T = {new (...args: any[]): unknown}; - AST_NODE_TYPES.TSMethodSignature, // type T = {f(...args: any[]): unknown}; AST_NODE_TYPES.TSDeclareFunction, // declare function _8(...args: any[]): unknown; + AST_NODE_TYPES.TSEmptyBodyFunctionExpression, // declare class A { f(...args: any[]): unknown; } + AST_NODE_TYPES.TSFunctionType, // type T = (...args: any[]) => unknown; + AST_NODE_TYPES.TSMethodSignature, // type T = {f(...args: any[]): unknown}; ].includes(node.type); } diff --git a/packages/eslint-plugin/src/rules/no-extra-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-extra-non-null-assertion.ts index ee1858fe7d95..fbeb009d2ba3 100644 --- a/packages/eslint-plugin/src/rules/no-extra-non-null-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-extra-non-null-assertion.ts @@ -11,10 +11,10 @@ export default createRule({ recommended: 'recommended', }, fixable: 'code', - schema: [], messages: { noExtraNonNullAssertion: 'Forbidden extra non-null assertion.', }, + schema: [], }, defaultOptions: [], create(context) { @@ -31,11 +31,11 @@ export default createRule({ } return { - 'TSNonNullExpression > TSNonNullExpression': checkExtraNonNullAssertion, - 'MemberExpression[optional = true] > TSNonNullExpression.object': - checkExtraNonNullAssertion, 'CallExpression[optional = true] > TSNonNullExpression.callee': checkExtraNonNullAssertion, + 'MemberExpression[optional = true] > TSNonNullExpression.object': + checkExtraNonNullAssertion, + 'TSNonNullExpression > TSNonNullExpression': checkExtraNonNullAssertion, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-extraneous-class.ts b/packages/eslint-plugin/src/rules/no-extraneous-class.ts index 854cec6b40eb..6a80ba9b876d 100644 --- a/packages/eslint-plugin/src/rules/no-extraneous-class.ts +++ b/packages/eslint-plugin/src/rules/no-extraneous-class.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -21,39 +22,39 @@ export default createRule({ description: 'Disallow classes used as namespaces', recommended: 'strict', }, + messages: { + empty: 'Unexpected empty class.', + onlyConstructor: 'Unexpected class with only a constructor.', + onlyStatic: 'Unexpected class with only static properties.', + }, schema: [ { type: 'object', additionalProperties: false, properties: { allowConstructorOnly: { + type: 'boolean', description: 'Whether to allow extraneous classes that contain only a constructor.', - type: 'boolean', }, allowEmpty: { + type: 'boolean', description: 'Whether to allow extraneous classes that have no body (i.e. are empty).', - type: 'boolean', }, allowStaticOnly: { + type: 'boolean', description: 'Whether to allow extraneous classes that only contain static members.', - type: 'boolean', }, allowWithDecorator: { + type: 'boolean', description: 'Whether to allow extraneous classes that include a decorator.', - type: 'boolean', }, }, }, ], - messages: { - empty: 'Unexpected empty class.', - onlyStatic: 'Unexpected class with only static properties.', - onlyConstructor: 'Unexpected class with only a constructor.', - }, }, defaultOptions: [ { diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 9c1cc46525c4..f0a526ad685b 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -1,9 +1,11 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; import type { TypeOrValueSpecifier } from '../util'; + import { createRule, getOperatorPrecedence, @@ -18,8 +20,8 @@ import { type Options = [ { - allowForKnownSafePromises?: TypeOrValueSpecifier[]; allowForKnownSafeCalls?: TypeOrValueSpecifier[]; + allowForKnownSafePromises?: TypeOrValueSpecifier[]; checkThenables?: boolean; ignoreIIFE?: boolean; ignoreVoid?: boolean; @@ -28,13 +30,13 @@ type Options = [ type MessageId = | 'floating' - | 'floatingVoid' - | 'floatingUselessRejectionHandler' - | 'floatingUselessRejectionHandlerVoid' | 'floatingFixAwait' | 'floatingFixVoid' | 'floatingPromiseArray' - | 'floatingPromiseArrayVoid'; + | 'floatingPromiseArrayVoid' + | 'floatingUselessRejectionHandler' + | 'floatingUselessRejectionHandlerVoid' + | 'floatingVoid'; const messageBase = 'Promises must be awaited, end with a call to .catch, or end with a call to .then with a rejection handler.'; @@ -56,6 +58,7 @@ const messagePromiseArrayVoid = export default createRule({ name: 'no-floating-promises', meta: { + type: 'problem', docs: { description: 'Require Promise-like statements to be handled appropriately', @@ -66,45 +69,44 @@ export default createRule({ messages: { floating: messageBase, floatingFixAwait: 'Add await operator.', - floatingVoid: messageBaseVoid, floatingFixVoid: 'Add void operator to ignore.', - floatingUselessRejectionHandler: `${messageBase} ${messageRejectionHandler}`, - floatingUselessRejectionHandlerVoid: `${messageBaseVoid} ${messageRejectionHandler}`, floatingPromiseArray: messagePromiseArray, floatingPromiseArrayVoid: messagePromiseArrayVoid, + floatingUselessRejectionHandler: `${messageBase} ${messageRejectionHandler}`, + floatingUselessRejectionHandlerVoid: `${messageBaseVoid} ${messageRejectionHandler}`, + floatingVoid: messageBaseVoid, }, schema: [ { type: 'object', + additionalProperties: false, properties: { - allowForKnownSafePromises: { - ...readonlynessOptionsSchema.properties.allow, - description: 'Type specifiers that are known to be safe to float.', - }, allowForKnownSafeCalls: { ...readonlynessOptionsSchema.properties.allow, description: 'Type specifiers of functions whose calls are safe to float.', }, + allowForKnownSafePromises: { + ...readonlynessOptionsSchema.properties.allow, + description: 'Type specifiers that are known to be safe to float.', + }, checkThenables: { + type: 'boolean', description: 'Whether to check all "Thenable"s, not just the built-in Promise type.', - type: 'boolean', - }, - ignoreVoid: { - description: 'Whether to ignore `void` expressions.', - type: 'boolean', }, ignoreIIFE: { + type: 'boolean', description: 'Whether to ignore async IIFEs (Immediately Invoked Function Expressions).', + }, + ignoreVoid: { type: 'boolean', + description: 'Whether to ignore `void` expressions.', }, }, - additionalProperties: false, }, ], - type: 'problem', }, defaultOptions: [ { diff --git a/packages/eslint-plugin/src/rules/no-for-in-array.ts b/packages/eslint-plugin/src/rules/no-for-in-array.ts index 6d104b639ebe..ec8ebe9125fb 100644 --- a/packages/eslint-plugin/src/rules/no-for-in-array.ts +++ b/packages/eslint-plugin/src/rules/no-for-in-array.ts @@ -11,6 +11,7 @@ import { getForStatementHeadLoc } from '../util/getForStatementHeadLoc'; export default createRule({ name: 'no-for-in-array', meta: { + type: 'problem', docs: { description: 'Disallow iterating over an array with a for-in loop', recommended: 'recommended', @@ -21,7 +22,6 @@ export default createRule({ 'For-in loops over arrays skips holes, returns indices as strings, and may visit the prototype chain or other enumerable properties. Use a more robust iteration method such as for-of or array.forEach instead.', }, schema: [], - type: 'problem', }, defaultOptions: [], create(context) { diff --git a/packages/eslint-plugin/src/rules/no-implied-eval.ts b/packages/eslint-plugin/src/rules/no-implied-eval.ts index 62a8d7cd102e..966c48f793ff 100644 --- a/packages/eslint-plugin/src/rules/no-implied-eval.ts +++ b/packages/eslint-plugin/src/rules/no-implied-eval.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -11,30 +12,30 @@ import { } from '../util'; const FUNCTION_CONSTRUCTOR = 'Function'; -const GLOBAL_CANDIDATES = new Set(['global', 'window', 'globalThis']); +const GLOBAL_CANDIDATES = new Set(['global', 'globalThis', 'window']); const EVAL_LIKE_METHODS = new Set([ + 'execScript', 'setImmediate', 'setInterval', 'setTimeout', - 'execScript', ]); export default createRule({ name: 'no-implied-eval', meta: { + type: 'suggestion', docs: { description: 'Disallow the use of `eval()`-like methods', - recommended: 'recommended', extendsBaseRule: true, + recommended: 'recommended', requiresTypeChecking: true, }, messages: { - noImpliedEvalError: 'Implied eval. Consider passing a function.', noFunctionConstructor: 'Implied eval. Do not use the Function constructor to create functions.', + noImpliedEvalError: 'Implied eval. Consider passing a function.', }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { @@ -156,8 +157,8 @@ export default createRule({ } return { - NewExpression: checkImpliedEval, CallExpression: checkImpliedEval, + NewExpression: checkImpliedEval, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts b/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts index 1658d471bc7b..12887dd2331e 100644 --- a/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts +++ b/packages/eslint-plugin/src/rules/no-import-type-side-effects.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { diff --git a/packages/eslint-plugin/src/rules/no-inferrable-types.ts b/packages/eslint-plugin/src/rules/no-inferrable-types.ts index 2b54267a503a..8c8c7c5b1dee 100644 --- a/packages/eslint-plugin/src/rules/no-inferrable-types.ts +++ b/packages/eslint-plugin/src/rules/no-inferrable-types.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/internal/prefer-ast-types-enum */ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; @@ -29,17 +30,17 @@ export default createRule({ schema: [ { type: 'object', + additionalProperties: false, properties: { ignoreParameters: { - description: 'Whether to ignore function parameters.', type: 'boolean', + description: 'Whether to ignore function parameters.', }, ignoreProperties: { - description: 'Whether to ignore class properties.', type: 'boolean', + description: 'Whether to ignore class properties.', }, }, - additionalProperties: false, }, ], }, @@ -99,8 +100,8 @@ export default createRule({ const keywordMap = { [AST_NODE_TYPES.TSBigIntKeyword]: 'bigint', [AST_NODE_TYPES.TSBooleanKeyword]: 'boolean', - [AST_NODE_TYPES.TSNumberKeyword]: 'number', [AST_NODE_TYPES.TSNullKeyword]: 'null', + [AST_NODE_TYPES.TSNumberKeyword]: 'number', [AST_NODE_TYPES.TSStringKeyword]: 'string', [AST_NODE_TYPES.TSSymbolKeyword]: 'symbol', [AST_NODE_TYPES.TSUndefinedKeyword]: 'undefined', @@ -277,11 +278,11 @@ export default createRule({ } return { - VariableDeclarator: inferrableVariableVisitor, - FunctionExpression: inferrableParameterVisitor, - FunctionDeclaration: inferrableParameterVisitor, ArrowFunctionExpression: inferrableParameterVisitor, + FunctionDeclaration: inferrableParameterVisitor, + FunctionExpression: inferrableParameterVisitor, PropertyDefinition: inferrablePropertyVisitor, + VariableDeclarator: inferrableVariableVisitor, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-invalid-this.ts b/packages/eslint-plugin/src/rules/no-invalid-this.ts index bf8d2a5c904b..db6b9489d98c 100644 --- a/packages/eslint-plugin/src/rules/no-invalid-this.ts +++ b/packages/eslint-plugin/src/rules/no-invalid-this.ts @@ -1,10 +1,12 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -22,8 +24,8 @@ export default createRule({ 'Disallow `this` keywords outside of classes or class-like objects', extendsBaseRule: true, }, - messages: baseRule.meta.messages, hasSuggestions: baseRule.meta.hasSuggestions, + messages: baseRule.meta.messages, schema: baseRule.meta.schema, }, defaultOptions: [{ capIsConstructor: true }], @@ -48,12 +50,6 @@ export default createRule({ return { ...rules, - PropertyDefinition(): void { - thisIsValidStack.push(true); - }, - 'PropertyDefinition:exit'(): void { - thisIsValidStack.pop(); - }, AccessorProperty(): void { thisIsValidStack.push(true); }, @@ -82,6 +78,12 @@ export default createRule({ 'FunctionExpression:exit'(): void { thisIsValidStack.pop(); }, + PropertyDefinition(): void { + thisIsValidStack.push(true); + }, + 'PropertyDefinition:exit'(): void { + thisIsValidStack.pop(); + }, ThisExpression(node: TSESTree.ThisExpression): void { const thisIsValidHere = thisIsValidStack[thisIsValidStack.length - 1]; diff --git a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts index 81faa510faf9..34f3f8cb22a1 100644 --- a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts +++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts @@ -1,11 +1,12 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; interface Options { - allowInGenericTypeArguments?: [string, ...string[]] | boolean; allowAsThisParameter?: boolean; + allowInGenericTypeArguments?: [string, ...string[]] | boolean; } type MessageIds = @@ -27,9 +28,9 @@ export default createRule<[Options], MessageIds>({ messages: { invalidVoidForGeneric: '{{ generic }} may not have void as a type argument.', + invalidVoidNotReturn: 'void is only valid as a return type.', invalidVoidNotReturnOrGeneric: 'void is only valid as a return type or generic type argument.', - invalidVoidNotReturn: 'void is only valid as a return type.', invalidVoidNotReturnOrThisParam: 'void is only valid as return type or type of `this` parameter.', invalidVoidNotReturnOrThisParamOrGeneric: @@ -40,7 +41,13 @@ export default createRule<[Options], MessageIds>({ schema: [ { type: 'object', + additionalProperties: false, properties: { + allowAsThisParameter: { + type: 'boolean', + description: + 'Whether a `this` parameter of a function may be `void`.', + }, allowInGenericTypeArguments: { description: 'Whether `void` can be used as a valid value for generic type parameters.', @@ -53,20 +60,14 @@ export default createRule<[Options], MessageIds>({ }, ], }, - allowAsThisParameter: { - description: - 'Whether a `this` parameter of a function may be `void`.', - type: 'boolean', - }, }, - additionalProperties: false, }, ], }, defaultOptions: [ - { allowInGenericTypeArguments: true, allowAsThisParameter: false }, + { allowAsThisParameter: false, allowInGenericTypeArguments: true }, ], - create(context, [{ allowInGenericTypeArguments, allowAsThisParameter }]) { + create(context, [{ allowAsThisParameter, allowInGenericTypeArguments }]) { const validParents: AST_NODE_TYPES[] = [ AST_NODE_TYPES.TSTypeAnnotation, // ]; @@ -115,9 +116,9 @@ export default createRule<[Options], MessageIds>({ .includes(fullyQualifiedName) ) { context.report({ + node, messageId: 'invalidVoidForGeneric', data: { generic: fullyQualifiedName }, - node, }); } return; @@ -125,10 +126,10 @@ export default createRule<[Options], MessageIds>({ if (!allowInGenericTypeArguments) { context.report({ + node, messageId: allowAsThisParameter ? 'invalidVoidNotReturnOrThisParam' : 'invalidVoidNotReturn', - node, }); } } @@ -142,8 +143,8 @@ export default createRule<[Options], MessageIds>({ ): void { if (parentNode.default !== node) { context.report({ - messageId: getNotReturnOrGenericMessageId(node), node, + messageId: getNotReturnOrGenericMessageId(node), }); } } @@ -217,6 +218,7 @@ export default createRule<[Options], MessageIds>({ } context.report({ + node, messageId: allowInGenericTypeArguments && allowAsThisParameter ? 'invalidVoidNotReturnOrThisParamOrGeneric' @@ -225,7 +227,6 @@ export default createRule<[Options], MessageIds>({ : allowAsThisParameter ? 'invalidVoidNotReturnOrThisParam' : 'invalidVoidNotReturn', - node, }); }, }; diff --git a/packages/eslint-plugin/src/rules/no-loop-func.ts b/packages/eslint-plugin/src/rules/no-loop-func.ts index a34217453b0d..244128db63e3 100644 --- a/packages/eslint-plugin/src/rules/no-loop-func.ts +++ b/packages/eslint-plugin/src/rules/no-loop-func.ts @@ -1,10 +1,12 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -23,11 +25,173 @@ export default createRule({ extendsBaseRule: true, }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: [], messages: baseRule.meta.messages, + schema: [], }, defaultOptions: [], create(context) { + const SKIPPED_IIFE_NODES = new Set< + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + >(); + + /** + * Gets the containing loop node of a specified node. + * + * We don't need to check nested functions, so this ignores those. + * `Scope.through` contains references of nested functions. + * + * @param node An AST node to get. + * @returns The containing loop node of the specified node, or `null`. + */ + function getContainingLoopNode(node: TSESTree.Node): TSESTree.Node | null { + for ( + let currentNode = node; + currentNode.parent; + currentNode = currentNode.parent + ) { + const parent = currentNode.parent; + + switch (parent.type) { + case AST_NODE_TYPES.WhileStatement: + case AST_NODE_TYPES.DoWhileStatement: + return parent; + + case AST_NODE_TYPES.ForStatement: + // `init` is outside of the loop. + if (parent.init !== currentNode) { + return parent; + } + break; + + case AST_NODE_TYPES.ForInStatement: + case AST_NODE_TYPES.ForOfStatement: + // `right` is outside of the loop. + if (parent.right !== currentNode) { + return parent; + } + break; + + case AST_NODE_TYPES.ArrowFunctionExpression: + case AST_NODE_TYPES.FunctionExpression: + case AST_NODE_TYPES.FunctionDeclaration: + // We don't need to check nested functions. + + // We need to check nested functions only in case of IIFE. + if (SKIPPED_IIFE_NODES.has(parent)) { + break; + } + return null; + + default: + break; + } + } + + return null; + } + + /** + * Gets the containing loop node of a given node. + * If the loop was nested, this returns the most outer loop. + * @param node A node to get. This is a loop node. + * @param excludedNode A node that the result node should not include. + * @returns The most outer loop node. + */ + function getTopLoopNode( + node: TSESTree.Node, + excludedNode: TSESTree.Node | null | undefined, + ): TSESTree.Node { + const border = excludedNode ? excludedNode.range[1] : 0; + let retv = node; + let containingLoopNode: TSESTree.Node | null = node; + + while (containingLoopNode && containingLoopNode.range[0] >= border) { + retv = containingLoopNode; + containingLoopNode = getContainingLoopNode(containingLoopNode); + } + + return retv; + } + + /** + * Checks whether a given reference which refers to an upper scope's variable is + * safe or not. + * @param loopNode A containing loop node. + * @param reference A reference to check. + * @returns `true` if the reference is safe or not. + */ + function isSafe( + loopNode: TSESTree.Node, + reference: TSESLint.Scope.Reference, + ): boolean { + const variable = reference.resolved; + const definition = variable?.defs[0]; + const declaration = definition?.parent; + const kind = + declaration?.type === AST_NODE_TYPES.VariableDeclaration + ? declaration.kind + : ''; + + // type references are all safe + // this only really matters for global types that haven't been configured + if (reference.isTypeReference) { + return true; + } + + // Variables which are declared by `const` is safe. + if (kind === 'const') { + return true; + } + + /* + * Variables which are declared by `let` in the loop is safe. + * It's a different instance from the next loop step's. + */ + if ( + kind === 'let' && + declaration && + declaration.range[0] > loopNode.range[0] && + declaration.range[1] < loopNode.range[1] + ) { + return true; + } + + /* + * WriteReferences which exist after this border are unsafe because those + * can modify the variable. + */ + const border = getTopLoopNode( + loopNode, + kind === 'let' ? declaration : null, + ).range[0]; + + /** + * Checks whether a given reference is safe or not. + * The reference is every reference of the upper scope's variable we are + * looking now. + * + * It's safe if the reference matches one of the following condition. + * - is readonly. + * - doesn't exist inside a local function and after the border. + * + * @param upperRef A reference to check. + * @returns `true` if the reference is safe. + */ + function isSafeReference(upperRef: TSESLint.Scope.Reference): boolean { + const id = upperRef.identifier; + + return ( + !upperRef.isWrite() || + (variable?.scope.variableScope === upperRef.from.variableScope && + id.range[0] < border) + ); + } + + return variable?.references.every(isSafeReference) ?? false; + } + /** * Reports functions which match the following condition: * - has a loop node in ancestors. @@ -48,8 +212,25 @@ export default createRule({ } const references = context.sourceCode.getScope(node).through; + + if (!(node.async || node.generator) && isIIFE(node)) { + const isFunctionExpression = + node.type === AST_NODE_TYPES.FunctionExpression; + + // Check if the function is referenced elsewhere in the code + const isFunctionReferenced = + isFunctionExpression && node.id + ? references.some(r => r.identifier.name === node.id?.name) + : false; + + if (!isFunctionReferenced) { + SKIPPED_IIFE_NODES.add(node); + return; + } + } + const unsafeRefs = references - .filter(r => !isSafe(loopNode, r)) + .filter(r => r.resolved && !isSafe(loopNode, r)) .map(r => r.identifier.name); if (unsafeRefs.length > 0) { @@ -63,157 +244,20 @@ export default createRule({ return { ArrowFunctionExpression: checkForLoops, - FunctionExpression: checkForLoops, FunctionDeclaration: checkForLoops, + FunctionExpression: checkForLoops, }; }, }); -/** - * Gets the containing loop node of a specified node. - * - * We don't need to check nested functions, so this ignores those. - * `Scope.through` contains references of nested functions. - * - * @param node An AST node to get. - * @returns The containing loop node of the specified node, or `null`. - */ -function getContainingLoopNode(node: TSESTree.Node): TSESTree.Node | null { - for ( - let currentNode = node; - currentNode.parent; - currentNode = currentNode.parent - ) { - const parent = currentNode.parent; - - switch (parent.type) { - case AST_NODE_TYPES.WhileStatement: - case AST_NODE_TYPES.DoWhileStatement: - return parent; - - case AST_NODE_TYPES.ForStatement: - // `init` is outside of the loop. - if (parent.init !== currentNode) { - return parent; - } - break; - - case AST_NODE_TYPES.ForInStatement: - case AST_NODE_TYPES.ForOfStatement: - // `right` is outside of the loop. - if (parent.right !== currentNode) { - return parent; - } - break; - - case AST_NODE_TYPES.ArrowFunctionExpression: - case AST_NODE_TYPES.FunctionExpression: - case AST_NODE_TYPES.FunctionDeclaration: - // We don't need to check nested functions. - return null; - - default: - break; - } - } - - return null; -} - -/** - * Gets the containing loop node of a given node. - * If the loop was nested, this returns the most outer loop. - * @param node A node to get. This is a loop node. - * @param excludedNode A node that the result node should not include. - * @returns The most outer loop node. - */ -function getTopLoopNode( - node: TSESTree.Node, - excludedNode: TSESTree.Node | null | undefined, -): TSESTree.Node { - const border = excludedNode ? excludedNode.range[1] : 0; - let retv = node; - let containingLoopNode: TSESTree.Node | null = node; - - while (containingLoopNode && containingLoopNode.range[0] >= border) { - retv = containingLoopNode; - containingLoopNode = getContainingLoopNode(containingLoopNode); - } - - return retv; -} - -/** - * Checks whether a given reference which refers to an upper scope's variable is - * safe or not. - * @param loopNode A containing loop node. - * @param reference A reference to check. - * @returns `true` if the reference is safe or not. - */ -function isSafe( - loopNode: TSESTree.Node, - reference: TSESLint.Scope.Reference, +function isIIFE( + node: + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression, ): boolean { - const variable = reference.resolved; - const definition = variable?.defs[0]; - const declaration = definition?.parent; - const kind = - declaration?.type === AST_NODE_TYPES.VariableDeclaration - ? declaration.kind - : ''; - - // type references are all safe - // this only really matters for global types that haven't been configured - if (reference.isTypeReference) { - return true; - } - - // Variables which are declared by `const` is safe. - if (kind === 'const') { - return true; - } - - /* - * Variables which are declared by `let` in the loop is safe. - * It's a different instance from the next loop step's. - */ - if ( - kind === 'let' && - declaration && - declaration.range[0] > loopNode.range[0] && - declaration.range[1] < loopNode.range[1] - ) { - return true; - } - - /* - * WriteReferences which exist after this border are unsafe because those - * can modify the variable. - */ - const border = getTopLoopNode(loopNode, kind === 'let' ? declaration : null) - .range[0]; - - /** - * Checks whether a given reference is safe or not. - * The reference is every reference of the upper scope's variable we are - * looking now. - * - * It's safe if the reference matches one of the following condition. - * - is readonly. - * - doesn't exist inside a local function and after the border. - * - * @param upperRef A reference to check. - * @returns `true` if the reference is safe. - */ - function isSafeReference(upperRef: TSESLint.Scope.Reference): boolean { - const id = upperRef.identifier; - - return ( - !upperRef.isWrite() || - (variable?.scope.variableScope === upperRef.from.variableScope && - id.range[0] < border) - ); - } - - return variable?.references.every(isSafeReference) ?? false; + return ( + node.parent.type === AST_NODE_TYPES.CallExpression && + node.parent.callee === node + ); } diff --git a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts index 000e5d9aa0bc..b3659ddd68bd 100644 --- a/packages/eslint-plugin/src/rules/no-loss-of-precision.ts +++ b/packages/eslint-plugin/src/rules/no-loss-of-precision.ts @@ -2,6 +2,7 @@ import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -20,8 +21,8 @@ export default createRule({ extendsBaseRule: true, }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: [], messages: baseRule.meta.messages, + schema: [], }, defaultOptions: [], create(context) { diff --git a/packages/eslint-plugin/src/rules/no-magic-numbers.ts b/packages/eslint-plugin/src/rules/no-magic-numbers.ts index 2bfd2e50bc36..75c79f852444 100644 --- a/packages/eslint-plugin/src/rules/no-magic-numbers.ts +++ b/packages/eslint-plugin/src/rules/no-magic-numbers.ts @@ -1,11 +1,13 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule, deepMerge } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -22,22 +24,22 @@ const schema = deepMerge( : baseRule.meta.schema, { properties: { - ignoreNumericLiteralTypes: { - description: - 'Whether numbers used in TypeScript numeric literal types are considered okay.', - type: 'boolean', - }, ignoreEnums: { + type: 'boolean', description: 'Whether enums used in TypeScript are considered okay.', + }, + ignoreNumericLiteralTypes: { type: 'boolean', + description: + 'Whether numbers used in TypeScript numeric literal types are considered okay.', }, ignoreReadonlyClassProperties: { - description: 'Whether `readonly` class properties are considered okay.', type: 'boolean', + description: 'Whether `readonly` class properties are considered okay.', }, ignoreTypeIndexes: { - description: 'Whether numbers used to index types are okay.', type: 'boolean', + description: 'Whether numbers used to index types are okay.', }, }, }, @@ -51,17 +53,17 @@ export default createRule({ description: 'Disallow magic numbers', extendsBaseRule: true, }, - schema: [schema], messages: baseRule.meta.messages, + schema: [schema], }, defaultOptions: [ { + detectObjects: false, + enforceConst: false, ignore: [], ignoreArrayIndexes: false, - enforceConst: false, - detectObjects: false, - ignoreNumericLiteralTypes: false, ignoreEnums: false, + ignoreNumericLiteralTypes: false, ignoreReadonlyClassProperties: false, ignoreTypeIndexes: false, }, @@ -126,8 +128,8 @@ export default createRule({ } context.report({ - messageId: 'noMagic', node: fullNumberNode, + messageId: 'noMagic', data: { raw }, }); @@ -163,7 +165,7 @@ function normalizeIgnoreValue( */ function normalizeLiteralValue( node: TSESTree.BigIntLiteral | TSESTree.NumberLiteral, - value: number | bigint, + value: bigint | number, ): bigint | number { if ( node.parent.type === AST_NODE_TYPES.UnaryExpression && diff --git a/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts b/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts index a75bb04afc13..3529b02998de 100644 --- a/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts +++ b/packages/eslint-plugin/src/rules/no-meaningless-void-operator.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { ESLintUtils } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -31,15 +32,15 @@ export default createRule({ schema: [ { type: 'object', + additionalProperties: false, properties: { checkNever: { - description: - 'Whether to suggest removing `void` when the argument has type `never`.', type: 'boolean', default: false, + description: + 'Whether to suggest removing `void` when the argument has type `never`.', }, }, - additionalProperties: false, }, ], }, diff --git a/packages/eslint-plugin/src/rules/no-misused-new.ts b/packages/eslint-plugin/src/rules/no-misused-new.ts index 48c6847a848b..46c8462ff59a 100644 --- a/packages/eslint-plugin/src/rules/no-misused-new.ts +++ b/packages/eslint-plugin/src/rules/no-misused-new.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -11,11 +12,11 @@ export default createRule({ description: 'Enforce valid definition of `new` and `constructor`', recommended: 'recommended', }, - schema: [], messages: { - errorMessageInterface: 'Interfaces cannot be constructed, only classes.', errorMessageClass: 'Class cannot have method named `new`.', + errorMessageInterface: 'Interfaces cannot be constructed, only classes.', }, + schema: [], }, defaultOptions: [], create(context) { @@ -51,10 +52,10 @@ export default createRule({ */ function isMatchingParentType( parent: - | TSESTree.TSInterfaceDeclaration | TSESTree.ClassDeclaration | TSESTree.ClassExpression | TSESTree.Identifier + | TSESTree.TSInterfaceDeclaration | undefined, returnType: TSESTree.TSTypeAnnotation | undefined, ): boolean { @@ -71,6 +72,19 @@ export default createRule({ } return { + "ClassBody > MethodDefinition[key.name='new']"( + node: TSESTree.MethodDefinition, + ): void { + if ( + node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression && + isMatchingParentType(node.parent.parent, node.value.returnType) + ) { + context.report({ + node, + messageId: 'errorMessageClass', + }); + } + }, 'TSInterfaceBody > TSConstructSignatureDeclaration'( node: TSESTree.TSConstructSignatureDeclaration, ): void { @@ -95,19 +109,6 @@ export default createRule({ messageId: 'errorMessageInterface', }); }, - "ClassBody > MethodDefinition[key.name='new']"( - node: TSESTree.MethodDefinition, - ): void { - if ( - node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression && - isMatchingParentType(node.parent.parent, node.value.returnType) - ) { - context.report({ - node, - messageId: 'errorMessageClass', - }); - } - }, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index dd219fa95d3f..ac8c359fc1cc 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -16,8 +17,8 @@ import { type Options = [ { checksConditionals?: boolean; - checksVoidReturn?: ChecksVoidReturnOptions | boolean; checksSpreads?: boolean; + checksVoidReturn?: ChecksVoidReturnOptions | boolean; }, ]; @@ -74,12 +75,16 @@ function parseChecksVoidReturn( export default createRule({ name: 'no-misused-promises', meta: { + type: 'problem', docs: { description: 'Disallow Promises in places not designed to handle them', recommended: 'recommended', requiresTypeChecking: true, }, messages: { + conditional: 'Expected non-Promise value in a boolean conditional.', + predicate: 'Expected a non-Promise value to be returned.', + spread: 'Expected a non-Promise value to be spreaded in an object.', voidReturnArgument: 'Promise returned in function argument where a void return was expected.', voidReturnAttribute: @@ -92,9 +97,6 @@ export default createRule({ 'Promise-returning function provided to return value where a void return was expected.', voidReturnVariable: 'Promise-returning function provided to variable where a void return was expected.', - conditional: 'Expected non-Promise value in a boolean conditional.', - predicate: 'Expected a non-Promise value to be returned.', - spread: 'Expected a non-Promise value to be spreaded in an object.', }, schema: [ { @@ -104,71 +106,71 @@ export default createRule({ checksConditionals: { type: 'boolean', }, + checksSpreads: { + type: 'boolean', + description: 'Whether to warn when `...` spreading a `Promise`.', + }, checksVoidReturn: { oneOf: [ { type: 'boolean' }, { + type: 'object', additionalProperties: false, properties: { arguments: { + type: 'boolean', description: 'Disables checking an asynchronous function passed as argument where the parameter type expects a function that returns `void`.', - type: 'boolean', }, attributes: { + type: 'boolean', description: 'Disables checking an asynchronous function passed as a JSX attribute expected to be a function that returns `void`.', - type: 'boolean', }, inheritedMethods: { + type: 'boolean', description: 'Disables checking an asynchronous method in a type that extends or implements another type expecting that method to return `void`.', - type: 'boolean', }, properties: { + type: 'boolean', description: 'Disables checking an asynchronous function passed as an object property expected to be a function that returns `void`.', - type: 'boolean', }, returns: { + type: 'boolean', description: 'Disables checking an asynchronous function returned in a function whose return type is a function that returns `void`.', - type: 'boolean', }, variables: { + type: 'boolean', description: 'Disables checking an asynchronous function used as a variable whose return type is a function that returns `void`.', - type: 'boolean', }, }, - type: 'object', }, ], }, - checksSpreads: { - description: 'Whether to warn when `...` spreading a `Promise`.', - type: 'boolean', - }, }, }, ], - type: 'problem', }, defaultOptions: [ { checksConditionals: true, - checksVoidReturn: true, checksSpreads: true, + checksVoidReturn: true, }, ], - create(context, [{ checksConditionals, checksVoidReturn, checksSpreads }]) { + create(context, [{ checksConditionals, checksSpreads, checksVoidReturn }]) { const services = getParserServices(context); const checker = services.program.getTypeChecker(); const checkedNodes = new Set(); const conditionalChecks: TSESLint.RuleListener = { + 'CallExpression > MemberExpression': checkArrayPredicates, ConditionalExpression: checkTestConditional, DoWhileStatement: checkTestConditional, ForStatement: checkTestConditional, @@ -178,7 +180,6 @@ export default createRule({ checkConditional(node.argument, true); }, WhileStatement: checkTestConditional, - 'CallExpression > MemberExpression': checkArrayPredicates, }; checksVoidReturn = parseChecksVoidReturn(checksVoidReturn); @@ -320,8 +321,8 @@ export default createRule({ const tsNode = services.esTreeNodeToTSNodeMap.get(node); if (isAlwaysThenable(checker, tsNode)) { context.report({ - messageId: 'conditional', node, + messageId: 'conditional', }); } } @@ -337,8 +338,8 @@ export default createRule({ const type = services.esTreeNodeToTSNodeMap.get(callback); if (returnsThenable(checker, type)) { context.report({ - messageId: 'predicate', node: callback, + messageId: 'predicate', }); } } @@ -362,8 +363,8 @@ export default createRule({ const tsNode = services.esTreeNodeToTSNodeMap.get(argument); if (returnsThenable(checker, tsNode as ts.Expression)) { context.report({ - messageId: 'voidReturnArgument', node: argument, + messageId: 'voidReturnArgument', }); } } @@ -378,8 +379,8 @@ export default createRule({ if (returnsThenable(checker, tsNode.right)) { context.report({ - messageId: 'voidReturnVariable', node: node.right, + messageId: 'voidReturnVariable', }); } } @@ -406,8 +407,8 @@ export default createRule({ if (returnsThenable(checker, tsNode.initializer)) { context.report({ - messageId: 'voidReturnVariable', node: node.init, + messageId: 'voidReturnVariable', }); } } @@ -426,8 +427,8 @@ export default createRule({ returnsThenable(checker, tsNode.initializer) ) { context.report({ - messageId: 'voidReturnProperty', node: node.value, + messageId: 'voidReturnProperty', }); } } else if (ts.isShorthandPropertyAssignment(tsNode)) { @@ -438,8 +439,8 @@ export default createRule({ returnsThenable(checker, tsNode.name) ) { context.report({ - messageId: 'voidReturnProperty', node: node.value, + messageId: 'voidReturnProperty', }); } } else if (ts.isMethodDeclaration(tsNode)) { @@ -480,8 +481,8 @@ export default createRule({ if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) { context.report({ - messageId: 'voidReturnProperty', node: node.value, + messageId: 'voidReturnProperty', }); } return; @@ -520,8 +521,8 @@ export default createRule({ returnsThenable(checker, tsNode.expression) ) { context.report({ - messageId: 'voidReturnReturnValue', node: node.argument, + messageId: 'voidReturnReturnValue', }); } } @@ -621,8 +622,8 @@ export default createRule({ returnsThenable(checker, expression) ) { context.report({ - messageId: 'voidReturnAttribute', node: node.value, + messageId: 'voidReturnAttribute', }); } } @@ -632,8 +633,8 @@ export default createRule({ if (isSometimesThenable(checker, tsNode.expression)) { context.report({ - messageId: 'spread', node: node.argument, + messageId: 'spread', }); } } diff --git a/packages/eslint-plugin/src/rules/no-mixed-enums.ts b/packages/eslint-plugin/src/rules/no-mixed-enums.ts index ce6a706a10d0..6d99f22e3f27 100644 --- a/packages/eslint-plugin/src/rules/no-mixed-enums.ts +++ b/packages/eslint-plugin/src/rules/no-mixed-enums.ts @@ -1,6 +1,7 @@ import type { Scope } from '@typescript-eslint/scope-manager'; -import { DefinitionType } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; + +import { DefinitionType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -16,6 +17,7 @@ enum AllowedType { export default createRule({ name: 'no-mixed-enums', meta: { + type: 'problem', docs: { description: 'Disallow enums from having both number and string members', recommended: 'strict', @@ -25,7 +27,6 @@ export default createRule({ mixed: `Mixing number and string enums can be confusing.`, }, schema: [], - type: 'problem', }, defaultOptions: [], create(context) { @@ -208,8 +209,8 @@ export default createRule({ if (currentType !== desiredType) { context.report({ - messageId: 'mixed', node: member.initializer ?? member, + messageId: 'mixed', }); return; } diff --git a/packages/eslint-plugin/src/rules/no-namespace.ts b/packages/eslint-plugin/src/rules/no-namespace.ts index c6b9213259be..38567e5c90d1 100644 --- a/packages/eslint-plugin/src/rules/no-namespace.ts +++ b/packages/eslint-plugin/src/rules/no-namespace.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isDefinitionFile } from '../util'; @@ -26,19 +27,19 @@ export default createRule({ schema: [ { type: 'object', + additionalProperties: false, properties: { allowDeclarations: { + type: 'boolean', description: 'Whether to allow `declare` with custom TypeScript namespaces.', - type: 'boolean', }, allowDefinitionFiles: { + type: 'boolean', description: 'Whether to allow `declare` with custom TypeScript namespaces inside definition files.', - type: 'boolean', }, }, - additionalProperties: false, }, ], }, diff --git a/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts index 1369254b16ae..7aa673541bfb 100644 --- a/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/no-non-null-asserted-nullish-coalescing.ts @@ -1,6 +1,7 @@ import type { Definition } from '@typescript-eslint/scope-manager'; -import { DefinitionType } from '@typescript-eslint/scope-manager'; import type { TSESLint } from '@typescript-eslint/utils'; + +import { DefinitionType } from '@typescript-eslint/scope-manager'; import { ASTUtils, TSESTree } from '@typescript-eslint/utils'; import { createRule, nullThrows, NullThrowsReasons } from '../util'; @@ -38,13 +39,13 @@ export default createRule({ 'Disallow non-null assertions in the left operand of a nullish coalescing operator', recommended: 'strict', }, + hasSuggestions: true, messages: { noNonNullAssertedNullishCoalescing: 'The nullish coalescing operator is designed to handle undefined and null - using a non-null assertion is not needed.', suggestRemovingNonNull: 'Remove the non-null assertion.', }, schema: [], - hasSuggestions: true, }, defaultOptions: [], create(context) { diff --git a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts index 45291b40808f..d31e9a03da63 100644 --- a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts @@ -1,4 +1,5 @@ import type { TSESLint } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { diff --git a/packages/eslint-plugin/src/rules/no-redeclare.ts b/packages/eslint-plugin/src/rules/no-redeclare.ts index a2060a0ea4da..99051a7b463f 100644 --- a/packages/eslint-plugin/src/rules/no-redeclare.ts +++ b/packages/eslint-plugin/src/rules/no-redeclare.ts @@ -1,5 +1,6 @@ -import { ScopeType } from '@typescript-eslint/scope-manager'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + +import { ScopeType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, getNameLocationInGlobalDirectiveComment } from '../util'; @@ -20,31 +21,31 @@ export default createRule({ description: 'Disallow variable redeclaration', extendsBaseRule: true, }, + messages: { + redeclared: "'{{id}}' is already defined.", + redeclaredAsBuiltin: + "'{{id}}' is already defined as a built-in global variable.", + redeclaredBySyntax: + "'{{id}}' is already defined by a variable declaration.", + }, schema: [ { type: 'object', + additionalProperties: false, properties: { builtinGlobals: { + type: 'boolean', description: 'Whether to report shadowing of built-in global variables.', - type: 'boolean', }, ignoreDeclarationMerge: { + type: 'boolean', description: 'Whether to ignore declaration merges between certain TypeScript declaration types.', - type: 'boolean', }, }, - additionalProperties: false, }, ], - messages: { - redeclared: "'{{id}}' is already defined.", - redeclaredAsBuiltin: - "'{{id}}' is already defined as a built-in global variable.", - redeclaredBySyntax: - "'{{id}}' is already defined by a variable declaration.", - }, }, defaultOptions: [ { @@ -54,13 +55,13 @@ export default createRule({ ], create(context, [options]) { const CLASS_DECLARATION_MERGE_NODES = new Set([ + AST_NODE_TYPES.ClassDeclaration, AST_NODE_TYPES.TSInterfaceDeclaration, AST_NODE_TYPES.TSModuleDeclaration, - AST_NODE_TYPES.ClassDeclaration, ]); const FUNCTION_DECLARATION_MERGE_NODES = new Set([ - AST_NODE_TYPES.TSModuleDeclaration, AST_NODE_TYPES.FunctionDeclaration, + AST_NODE_TYPES.TSModuleDeclaration, ]); const ENUM_DECLARATION_MERGE_NODES = new Set([ AST_NODE_TYPES.TSEnumDeclaration, @@ -69,9 +70,9 @@ export default createRule({ function* iterateDeclarations(variable: TSESLint.Scope.Variable): Generator< { - type: 'builtin' | 'comment' | 'syntax'; - node?: TSESTree.Comment | TSESTree.Identifier; loc?: TSESTree.SourceLocation; + node?: TSESTree.Comment | TSESTree.Identifier; + type: 'builtin' | 'comment' | 'syntax'; }, void > { @@ -90,13 +91,13 @@ export default createRule({ ) { for (const comment of variable.eslintExplicitGlobalComments) { yield { - type: 'comment', - node: comment, loc: getNameLocationInGlobalDirectiveComment( context.sourceCode, comment, variable.name, ), + node: comment, + type: 'comment', }; } } @@ -147,7 +148,7 @@ export default createRule({ // there's more than one class declaration, which needs to be reported for (const { identifier } of classDecls) { - yield { type: 'syntax', node: identifier, loc: identifier.loc }; + yield { loc: identifier.loc, node: identifier, type: 'syntax' }; } return; } @@ -168,7 +169,7 @@ export default createRule({ // there's more than one function declaration, which needs to be reported for (const { identifier } of functionDecls) { - yield { type: 'syntax', node: identifier, loc: identifier.loc }; + yield { loc: identifier.loc, node: identifier, type: 'syntax' }; } return; } @@ -189,14 +190,14 @@ export default createRule({ // there's more than one enum declaration, which needs to be reported for (const { identifier } of enumDecls) { - yield { type: 'syntax', node: identifier, loc: identifier.loc }; + yield { loc: identifier.loc, node: identifier, type: 'syntax' }; } return; } } for (const { identifier } of identifiers) { - yield { type: 'syntax', node: identifier, loc: identifier.loc }; + yield { loc: identifier.loc, node: identifier, type: 'syntax' }; } } @@ -221,12 +222,12 @@ export default createRule({ const data = { id: variable.name }; // Report extra declarations. - for (const { type, node, loc } of extraDeclarations) { + for (const { loc, node, type } of extraDeclarations) { const messageId = type === declaration.type ? 'redeclared' : detailMessageId; if (node) { - context.report({ node, loc, messageId, data }); + context.report({ loc, node, messageId, data }); } else if (loc) { context.report({ loc, messageId, data }); } @@ -250,6 +251,15 @@ export default createRule({ } return { + ArrowFunctionExpression: checkForBlock, + + BlockStatement: checkForBlock, + ForInStatement: checkForBlock, + ForOfStatement: checkForBlock, + + ForStatement: checkForBlock, + FunctionDeclaration: checkForBlock, + FunctionExpression: checkForBlock, Program(node): void { const scope = context.sourceCode.getScope(node); @@ -265,15 +275,6 @@ export default createRule({ findVariablesInScope(scope.childScopes[0]); } }, - - FunctionDeclaration: checkForBlock, - FunctionExpression: checkForBlock, - ArrowFunctionExpression: checkForBlock, - - BlockStatement: checkForBlock, - ForStatement: checkForBlock, - ForInStatement: checkForBlock, - ForOfStatement: checkForBlock, SwitchStatement: checkForBlock, }; }, diff --git a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts index 3d63392bebc9..b550802df58d 100644 --- a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts @@ -56,9 +56,9 @@ const keywordNodeTypesToTsTypes = new Map([ [TSESTree.AST_NODE_TYPES.TSBigIntKeyword, ts.TypeFlags.BigInt], [TSESTree.AST_NODE_TYPES.TSBooleanKeyword, ts.TypeFlags.Boolean], [TSESTree.AST_NODE_TYPES.TSNeverKeyword, ts.TypeFlags.Never], - [TSESTree.AST_NODE_TYPES.TSUnknownKeyword, ts.TypeFlags.Unknown], [TSESTree.AST_NODE_TYPES.TSNumberKeyword, ts.TypeFlags.Number], [TSESTree.AST_NODE_TYPES.TSStringKeyword, ts.TypeFlags.String], + [TSESTree.AST_NODE_TYPES.TSUnknownKeyword, ts.TypeFlags.Unknown], ]); type PrimitiveTypeFlag = (typeof primitiveTypeFlags)[number]; @@ -193,6 +193,7 @@ function unionTypePartsUnlessBoolean(type: ts.Type): ts.Type[] { export default createRule({ name: 'no-redundant-type-constituents', meta: { + type: 'suggestion', docs: { description: 'Disallow members of unions and intersections that do nothing or override type information', @@ -200,14 +201,13 @@ export default createRule({ requiresTypeChecking: true, }, messages: { + errorTypeOverrides: `'{{typeName}}' is an 'error' type that acts as 'any' and overrides all other types in this {{container}} type.`, literalOverridden: `{{literal}} is overridden by {{primitive}} in this union type.`, - primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`, overridden: `'{{typeName}}' is overridden by other types in this {{container}} type.`, overrides: `'{{typeName}}' overrides all other types in this {{container}} type.`, - errorTypeOverrides: `'{{typeName}}' is an 'error' type that acts as 'any' and overrides all other types in this {{container}} type.`, + primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`, }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { @@ -292,15 +292,15 @@ export default createRule({ ] as const) { if (typeFlags === checkFlag) { context.report({ - data: { - container: 'intersection', - typeName, - }, + node: typeNode, messageId: typeFlags === ts.TypeFlags.Any && typeName !== 'any' ? 'errorTypeOverrides' : messageId, - node: typeNode, + data: { + container: 'intersection', + typeName, + }, }); return true; } @@ -369,6 +369,8 @@ export default createRule({ } if (Number.isInteger(primitive)) { context.report({ + node: typeRef, + messageId: 'primitiveOverridden', data: { literal: typeValues.map(name => name.typeName).join(' | '), primitive: @@ -376,8 +378,6 @@ export default createRule({ primitive as keyof typeof primitiveTypeFlagNames ], }, - messageId: 'primitiveOverridden', - node: typeRef, }); } } @@ -394,12 +394,12 @@ export default createRule({ if (matchedLiteralTypes) { for (const typeNode of typeNodes) { context.report({ + node: typeNode, + messageId: 'primitiveOverridden', data: { literal: matchedLiteralTypes.join(' | '), primitive: primitiveTypeFlagNames[primitiveTypeFlag], }, - messageId: 'primitiveOverridden', - node: typeNode, }); } } @@ -422,15 +422,15 @@ export default createRule({ ] as const) { if (typeFlags === checkFlag) { context.report({ - data: { - container: 'union', - typeName, - }, + node: typeNode, messageId: typeFlags === ts.TypeFlags.Any && typeName !== 'any' ? 'errorTypeOverrides' : 'overrides', - node: typeNode, + data: { + container: 'union', + typeName, + }, }); return true; } @@ -441,12 +441,12 @@ export default createRule({ !isNodeInsideReturnType(node) ) { context.report({ + node: typeNode, + messageId: 'overridden', data: { container: 'union', typeName: 'never', }, - messageId: 'overridden', - node: typeNode, }); return true; } @@ -519,12 +519,12 @@ export default createRule({ for (const [primitiveTypeFlag, pairs] of grouped) { context.report({ + node: typeNode, + messageId: 'literalOverridden', data: { literal: pairs.map(pair => pair.literalValue).join(' | '), primitive: primitiveTypeFlagNames[primitiveTypeFlag], }, - messageId: 'literalOverridden', - node: typeNode, }); } } diff --git a/packages/eslint-plugin/src/rules/no-require-imports.ts b/packages/eslint-plugin/src/rules/no-require-imports.ts index 64f626076c32..26dcbcd6c821 100644 --- a/packages/eslint-plugin/src/rules/no-require-imports.ts +++ b/packages/eslint-plugin/src/rules/no-require-imports.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import * as util from '../util'; @@ -19,26 +20,26 @@ export default util.createRule({ description: 'Disallow invocation of `require()`', recommended: 'recommended', }, + messages: { + noRequireImports: 'A `require()` style import is forbidden.', + }, schema: [ { type: 'object', + additionalProperties: false, properties: { allow: { type: 'array', - items: { type: 'string' }, description: 'Patterns of import paths to allow requiring from.', + items: { type: 'string' }, }, allowAsImport: { type: 'boolean', description: 'Allows `require` statements in import declarations.', }, }, - additionalProperties: false, }, ], - messages: { - noRequireImports: 'A `require()` style import is forbidden.', - }, }, defaultOptions: [{ allow: [], allowAsImport: false }], create(context, options) { diff --git a/packages/eslint-plugin/src/rules/no-restricted-imports.ts b/packages/eslint-plugin/src/rules/no-restricted-imports.ts index f61baa7ed396..d407a5f66df5 100644 --- a/packages/eslint-plugin/src/rules/no-restricted-imports.ts +++ b/packages/eslint-plugin/src/rules/no-restricted-imports.ts @@ -1,5 +1,4 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { JSONSchema4AnyOfSchema, JSONSchema4ArraySchema, @@ -11,12 +10,15 @@ import type { RuleListener, } from 'eslint/lib/rules/no-restricted-imports'; import type { Ignore } from 'ignore'; + +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import ignore from 'ignore'; import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -40,40 +42,40 @@ const baseSchema = baseRule.meta.schema as { anyOf: [ unknown, { - type: 'array'; items: [ { - type: 'object'; properties: { paths: { - type: 'array'; items: { anyOf: [ { type: 'string' }, { - type: 'object'; properties: JSONSchema4ObjectSchema['properties']; required: string[]; + type: 'object'; }, ]; }; + type: 'array'; }; patterns: { anyOf: [ - { type: 'array'; items: { type: 'string' } }, + { items: { type: 'string' }; type: 'array' }, { - type: 'array'; items: { - type: 'object'; properties: JSONSchema4ObjectSchema['properties']; required: string[]; + type: 'object'; }; + type: 'array'; }, ]; }; }; + type: 'object'; }, ]; + type: 'array'; }, ]; }; @@ -154,17 +156,17 @@ const schema: JSONSchema4AnyOfSchema = { arrayOfStringsOrObjects, { type: 'array', + additionalItems: false, items: [ { type: 'object', + additionalProperties: false, properties: { paths: arrayOfStringsOrObjects, patterns: arrayOfStringsOrObjectPatterns, }, - additionalProperties: false, }, ], - additionalItems: false, }, ], }; @@ -235,8 +237,8 @@ export default createRule({ description: 'Disallow specified modules when loaded by `import`', extendsBaseRule: true, }, - messages: baseRule.meta.messages, fixable: baseRule.meta.fixable, + messages: baseRule.meta.messages, schema, }, defaultOptions: [], @@ -308,6 +310,29 @@ export default createRule({ } return { + ExportAllDeclaration: rules.ExportAllDeclaration, + 'ExportNamedDeclaration[source]'( + node: { + source: NonNullable; + } & TSESTree.ExportNamedDeclaration, + ): void { + if ( + node.exportKind === 'type' || + (node.specifiers.length > 0 && + node.specifiers.every(specifier => specifier.exportKind === 'type')) + ) { + const importSource = node.source.value.trim(); + if ( + !isAllowedTypeImportPath(importSource) && + !isAllowedTypeImportPattern(importSource) + ) { + return rules.ExportNamedDeclaration(node); + } + } else { + return rules.ExportNamedDeclaration(node); + } + }, + ImportDeclaration: checkImportNode, TSImportEqualsDeclaration( node: TSESTree.TSImportEqualsDeclaration, ): void { @@ -317,9 +342,9 @@ export default createRule({ const synthesizedImport: TSESTree.ImportDeclaration = { ...node, type: AST_NODE_TYPES.ImportDeclaration, - source: node.moduleReference.expression, assertions: [], attributes: [], + source: node.moduleReference.expression, specifiers: [ { ...node.id, @@ -333,29 +358,6 @@ export default createRule({ return checkImportNode(synthesizedImport); } }, - ImportDeclaration: checkImportNode, - 'ExportNamedDeclaration[source]'( - node: TSESTree.ExportNamedDeclaration & { - source: NonNullable; - }, - ): void { - if ( - node.exportKind === 'type' || - (node.specifiers.length > 0 && - node.specifiers.every(specifier => specifier.exportKind === 'type')) - ) { - const importSource = node.source.value.trim(); - if ( - !isAllowedTypeImportPath(importSource) && - !isAllowedTypeImportPattern(importSource) - ) { - return rules.ExportNamedDeclaration(node); - } - } else { - return rules.ExportNamedDeclaration(node); - } - }, - ExportAllDeclaration: rules.ExportAllDeclaration, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-restricted-types.ts b/packages/eslint-plugin/src/rules/no-restricted-types.ts index c13f2838d85d..8192b9380c7e 100644 --- a/packages/eslint-plugin/src/rules/no-restricted-types.ts +++ b/packages/eslint-plugin/src/rules/no-restricted-types.ts @@ -1,24 +1,25 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, objectReduceKey } from '../util'; type Types = Record< string, - | boolean - | string | { - message: string; fixWith?: string; + message: string; suggest?: readonly string[]; } + | boolean + | string | null >; export type Options = [ { - types?: Types; extendDefaults?: boolean; + types?: Types; }, ]; @@ -36,7 +37,7 @@ function stringifyNode( } function getCustomMessage( - bannedType: string | true | { message?: string; fixWith?: string } | null, + bannedType: { fixWith?: string; message?: string } | true | string | null, ): string { if (!bannedType || bannedType === true) { return ''; @@ -82,13 +83,14 @@ export default createRule({ }, schema: [ { + type: 'object', $defs: { banConfig: { oneOf: [ { type: 'boolean', - enum: [true], description: 'Bans the type with the default message', + enum: [true], }, { type: 'string', @@ -96,29 +98,29 @@ export default createRule({ }, { type: 'object', + additionalProperties: false, description: 'Bans a type', properties: { - message: { - type: 'string', - description: 'Custom error message', - }, fixWith: { type: 'string', description: 'Type to autofix replace with. Note that autofixers can be applied automatically - so you need to be careful with this option.', }, + message: { + type: 'string', + description: 'Custom error message', + }, suggest: { type: 'array', - items: { type: 'string' }, description: 'Types to suggest replacing with.', + items: { type: 'string' }, }, }, - additionalProperties: false, }, ], }, }, - type: 'object', + additionalProperties: false, properties: { types: { type: 'object', @@ -127,7 +129,6 @@ export default createRule({ }, }, }, - additionalProperties: false, }, ], }, diff --git a/packages/eslint-plugin/src/rules/no-shadow.ts b/packages/eslint-plugin/src/rules/no-shadow.ts index 806f1829bf0d..6a3605e79b8e 100644 --- a/packages/eslint-plugin/src/rules/no-shadow.ts +++ b/packages/eslint-plugin/src/rules/no-shadow.ts @@ -1,5 +1,6 @@ -import { DefinitionType, ScopeType } from '@typescript-eslint/scope-manager'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + +import { DefinitionType, ScopeType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -11,9 +12,9 @@ type Options = [ allow?: string[]; builtinGlobals?: boolean; hoist?: 'all' | 'functions' | 'never'; + ignoreFunctionTypeParameterNameValueShadow?: boolean; ignoreOnInitialization?: boolean; ignoreTypeValueShadow?: boolean; - ignoreFunctionTypeParameterNameValueShadow?: boolean; }, ]; @@ -32,61 +33,61 @@ export default createRule({ 'Disallow variable declarations from shadowing variables declared in the outer scope', extendsBaseRule: true, }, + messages: { + noShadow: + "'{{name}}' is already declared in the upper scope on line {{shadowedLine}} column {{shadowedColumn}}.", + noShadowGlobal: "'{{name}}' is already a global variable.", + }, schema: [ { type: 'object', + additionalProperties: false, properties: { + allow: { + type: 'array', + description: 'Identifier names for which shadowing is allowed.', + items: { + type: 'string', + }, + }, builtinGlobals: { + type: 'boolean', description: 'Whether to report shadowing of built-in global variables.', - type: 'boolean', }, hoist: { + type: 'string', description: 'Whether to report shadowing before outer functions or variables are defined.', - type: 'string', enum: ['all', 'functions', 'never'], }, - allow: { - description: 'Identifier names for which shadowing is allowed.', - type: 'array', - items: { - type: 'string', - }, + ignoreFunctionTypeParameterNameValueShadow: { + type: 'boolean', + description: + 'Whether to ignore function parameters named the same as a variable.', }, ignoreOnInitialization: { + type: 'boolean', description: 'Whether to ignore the variable initializers when the shadowed variable is presumably still unitialized.', - type: 'boolean', }, ignoreTypeValueShadow: { - description: - 'Whether to ignore types named the same as a variable.', type: 'boolean', - }, - ignoreFunctionTypeParameterNameValueShadow: { description: - 'Whether to ignore function parameters named the same as a variable.', - type: 'boolean', + 'Whether to ignore types named the same as a variable.', }, }, - additionalProperties: false, }, ], - messages: { - noShadow: - "'{{name}}' is already declared in the upper scope on line {{shadowedLine}} column {{shadowedColumn}}.", - noShadowGlobal: "'{{name}}' is already a global variable.", - }, }, defaultOptions: [ { allow: [], builtinGlobals: false, hoist: 'functions', + ignoreFunctionTypeParameterNameValueShadow: true, ignoreOnInitialization: false, ignoreTypeValueShadow: true, - ignoreFunctionTypeParameterNameValueShadow: true, }, ], create(context, [options]) { @@ -431,14 +432,14 @@ export default createRule({ } } else if ( [ - AST_NODE_TYPES.FunctionDeclaration, - AST_NODE_TYPES.ClassDeclaration, - AST_NODE_TYPES.FunctionExpression, - AST_NODE_TYPES.ClassExpression, AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.CatchClause, - AST_NODE_TYPES.ImportDeclaration, + AST_NODE_TYPES.ClassDeclaration, + AST_NODE_TYPES.ClassExpression, AST_NODE_TYPES.ExportNamedDeclaration, + AST_NODE_TYPES.FunctionDeclaration, + AST_NODE_TYPES.FunctionExpression, + AST_NODE_TYPES.ImportDeclaration, ].includes(node.type) ) { break; @@ -526,13 +527,13 @@ export default createRule({ */ function getDeclaredLocation( variable: TSESLint.Scope.Variable, - ): { global: false; line: number; column: number } | { global: true } { + ): { column: number; global: false; line: number } | { global: true } { const identifier = variable.identifiers.at(0); if (identifier) { return { + column: identifier.loc.start.column + 1, global: false, line: identifier.loc.start.line, - column: identifier.loc.start.column + 1, }; } return { @@ -633,8 +634,8 @@ export default createRule({ messageId: 'noShadow', data: { name: variable.name, - shadowedLine: location.line, shadowedColumn: location.column, + shadowedLine: location.line, }, }), }); diff --git a/packages/eslint-plugin/src/rules/no-this-alias.ts b/packages/eslint-plugin/src/rules/no-this-alias.ts index 98006e56328d..842b6bb6edf9 100644 --- a/packages/eslint-plugin/src/rules/no-this-alias.ts +++ b/packages/eslint-plugin/src/rules/no-this-alias.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -19,20 +20,25 @@ export default createRule({ description: 'Disallow aliasing `this`', recommended: 'recommended', }, + messages: { + thisAssignment: "Unexpected aliasing of 'this' to local variable.", + thisDestructure: + "Unexpected aliasing of members of 'this' to local variables.", + }, schema: [ { type: 'object', additionalProperties: false, properties: { allowDestructuring: { + type: 'boolean', description: 'Whether to ignore destructurings, such as `const { props, state } = this`.', - type: 'boolean', }, allowedNames: { + type: 'array', description: 'Names to ignore, such as ["self"] for `const self = this;`.', - type: 'array', items: { type: 'string', }, @@ -40,11 +46,6 @@ export default createRule({ }, }, ], - messages: { - thisAssignment: "Unexpected aliasing of 'this' to local variable.", - thisDestructure: - "Unexpected aliasing of members of 'this' to local variables.", - }, }, defaultOptions: [ { diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts index 6459579060f4..c1b80dde0f05 100644 --- a/packages/eslint-plugin/src/rules/no-type-alias.ts +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -6,8 +7,8 @@ import { createRule } from '../util'; type Values = | 'always' | 'in-intersections' - | 'in-unions-and-intersections' | 'in-unions' + | 'in-unions-and-intersections' | 'never'; type Options = [ @@ -16,10 +17,10 @@ type Options = [ allowCallbacks?: 'always' | 'never'; allowConditionalTypes?: 'always' | 'never'; allowConstructors?: 'always' | 'never'; + allowGenerics?: 'always' | 'never'; allowLiterals?: Values; allowMappedTypes?: Values; allowTupleTypes?: Values; - allowGenerics?: 'always' | 'never'; }, ]; type MessageIds = 'noCompositionAlias' | 'noTypeAlias'; @@ -28,26 +29,27 @@ type CompositionType = | AST_NODE_TYPES.TSIntersectionType | AST_NODE_TYPES.TSUnionType; interface TypeWithLabel { - node: TSESTree.Node; compositionType: CompositionType | null; + node: TSESTree.Node; } export default createRule({ name: 'no-type-alias', meta: { - deprecated: true, type: 'suggestion', + deprecated: true, docs: { description: 'Disallow type aliases', // too opinionated to be recommended }, messages: { - noTypeAlias: 'Type {{alias}} are not allowed.', noCompositionAlias: '{{typeName}} in {{compositionType}} types are not allowed.', + noTypeAlias: 'Type {{alias}} are not allowed.', }, schema: [ { + type: 'object', $defs: { expandedOptions: { type: 'string', @@ -64,43 +66,42 @@ export default createRule({ enum: ['always', 'never'], }, }, - type: 'object', + additionalProperties: false, properties: { allowAliases: { - description: 'Whether to allow direct one-to-one type aliases.', $ref: '#/items/0/$defs/expandedOptions', + description: 'Whether to allow direct one-to-one type aliases.', }, allowCallbacks: { - description: 'Whether to allow type aliases for callbacks.', $ref: '#/items/0/$defs/simpleOptions', + description: 'Whether to allow type aliases for callbacks.', }, allowConditionalTypes: { - description: 'Whether to allow type aliases for conditional types.', $ref: '#/items/0/$defs/simpleOptions', + description: 'Whether to allow type aliases for conditional types.', }, allowConstructors: { + $ref: '#/items/0/$defs/simpleOptions', description: 'Whether to allow type aliases with constructors.', + }, + allowGenerics: { $ref: '#/items/0/$defs/simpleOptions', + description: 'Whether to allow type aliases with generic types.', }, allowLiterals: { + $ref: '#/items/0/$defs/expandedOptions', description: 'Whether to allow type aliases with object literal types.', - $ref: '#/items/0/$defs/expandedOptions', }, allowMappedTypes: { - description: 'Whether to allow type aliases with mapped types.', $ref: '#/items/0/$defs/expandedOptions', + description: 'Whether to allow type aliases with mapped types.', }, allowTupleTypes: { - description: 'Whether to allow type aliases with tuple types.', $ref: '#/items/0/$defs/expandedOptions', - }, - allowGenerics: { - description: 'Whether to allow type aliases with generic types.', - $ref: '#/items/0/$defs/simpleOptions', + description: 'Whether to allow type aliases with tuple types.', }, }, - additionalProperties: false, }, ], }, @@ -110,10 +111,10 @@ export default createRule({ allowCallbacks: 'never', allowConditionalTypes: 'never', allowConstructors: 'never', + allowGenerics: 'never', allowLiterals: 'never', allowMappedTypes: 'never', allowTupleTypes: 'never', - allowGenerics: 'never', }, ], create( @@ -124,10 +125,10 @@ export default createRule({ allowCallbacks, allowConditionalTypes, allowConstructors, + allowGenerics, allowLiterals, allowMappedTypes, allowTupleTypes, - allowGenerics, }, ], ) { @@ -145,11 +146,11 @@ export default createRule({ const aliasTypes = new Set([ AST_NODE_TYPES.TSArrayType, AST_NODE_TYPES.TSImportType, - AST_NODE_TYPES.TSTypeReference, - AST_NODE_TYPES.TSLiteralType, - AST_NODE_TYPES.TSTypeQuery, AST_NODE_TYPES.TSIndexedAccessType, + AST_NODE_TYPES.TSLiteralType, AST_NODE_TYPES.TSTemplateLiteralType, + AST_NODE_TYPES.TSTypeQuery, + AST_NODE_TYPES.TSTypeReference, ]); /** diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts b/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts index 17732e926868..9a0c38c47a80 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -14,8 +15,8 @@ type MessageIds = type Options = [ { - allowComparingNullableBooleansToTrue?: boolean; allowComparingNullableBooleansToFalse?: boolean; + allowComparingNullableBooleansToTrue?: boolean; }, ]; @@ -32,6 +33,7 @@ interface BooleanComparisonWithTypeInformation extends BooleanComparison { export default createRule({ name: 'no-unnecessary-boolean-literal-compare', meta: { + type: 'suggestion', docs: { description: 'Disallow unnecessary equality comparisons against boolean literals', @@ -40,41 +42,40 @@ export default createRule({ }, fixable: 'code', messages: { - direct: - 'This expression unnecessarily compares a boolean value to a boolean instead of using it directly.', - negated: - 'This expression unnecessarily compares a boolean value to a boolean instead of negating it.', + comparingNullableToFalse: + 'This expression unnecessarily compares a nullable boolean value to false instead of using the ?? operator to provide a default.', comparingNullableToTrueDirect: 'This expression unnecessarily compares a nullable boolean value to true instead of using it directly.', comparingNullableToTrueNegated: 'This expression unnecessarily compares a nullable boolean value to true instead of negating it.', - comparingNullableToFalse: - 'This expression unnecessarily compares a nullable boolean value to false instead of using the ?? operator to provide a default.', + direct: + 'This expression unnecessarily compares a boolean value to a boolean instead of using it directly.', + negated: + 'This expression unnecessarily compares a boolean value to a boolean instead of negating it.', }, schema: [ { type: 'object', + additionalProperties: false, properties: { - allowComparingNullableBooleansToTrue: { - description: - 'Whether to allow comparisons between nullable boolean variables and `true`.', - type: 'boolean', - }, allowComparingNullableBooleansToFalse: { + type: 'boolean', description: 'Whether to allow comparisons between nullable boolean variables and `false`.', + }, + allowComparingNullableBooleansToTrue: { type: 'boolean', + description: + 'Whether to allow comparisons between nullable boolean variables and `true`.', }, }, - additionalProperties: false, }, ], - type: 'suggestion', }, defaultOptions: [ { - allowComparingNullableBooleansToTrue: true, allowComparingNullableBooleansToFalse: true, + allowComparingNullableBooleansToTrue: true, }, ], create(context, [options]) { @@ -176,8 +177,8 @@ export default createRule({ const negated = !comparisonType.isPositive; return { - literalBooleanInComparison, expression, + literalBooleanInComparison, negated, }; } @@ -216,6 +217,16 @@ export default createRule({ } context.report({ + node, + messageId: comparison.expressionIsNullableBoolean + ? comparison.literalBooleanInComparison + ? comparison.negated + ? 'comparingNullableToTrueNegated' + : 'comparingNullableToTrueDirect' + : 'comparingNullableToFalse' + : comparison.negated + ? 'negated' + : 'direct', *fix(fixer) { // 1. isUnaryNegation - parent negation // 2. literalBooleanInComparison - is compared to literal boolean @@ -254,16 +265,6 @@ export default createRule({ yield fixer.insertTextAfter(mutatedNode, ' ?? true)'); } }, - messageId: comparison.expressionIsNullableBoolean - ? comparison.literalBooleanInComparison - ? comparison.negated - ? 'comparingNullableToTrueNegated' - : 'comparingNullableToTrueDirect' - : 'comparingNullableToFalse' - : comparison.negated - ? 'negated' - : 'direct', - node, }); }, }; @@ -277,27 +278,27 @@ interface EqualsKind { function getEqualsKind(operator: string): EqualsKind | undefined { switch (operator) { - case '==': + case '!=': return { - isPositive: true, + isPositive: false, isStrict: false, }; - case '===': + case '!==': return { - isPositive: true, + isPositive: false, isStrict: true, }; - case '!=': + case '==': return { - isPositive: false, + isPositive: true, isStrict: false, }; - case '!==': + case '===': return { - isPositive: false, + isPositive: true, isStrict: true, }; diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 4befec262769..f9d959e4950b 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -86,13 +87,13 @@ export type MessageId = | 'alwaysTruthy' | 'alwaysTruthyFunc' | 'literalBooleanExpression' - | 'typeGuardAlreadyIsType' - | 'replaceWithTrue' | 'never' | 'neverNullish' | 'neverOptionalChain' | 'noOverlapBooleanExpression' - | 'noStrictNullCheck'; + | 'noStrictNullCheck' + | 'replaceWithTrue' + | 'typeGuardAlreadyIsType'; export default createRule({ name: 'no-unnecessary-condition', @@ -104,53 +105,53 @@ export default createRule({ recommended: 'strict', requiresTypeChecking: true, }, + fixable: 'code', + messages: { + alwaysFalsy: 'Unnecessary conditional, value is always falsy.', + alwaysFalsyFunc: + 'This callback should return a conditional, but return is always falsy.', + alwaysNullish: + 'Unnecessary conditional, left-hand side of `??` operator is always `null` or `undefined`.', + alwaysTruthy: 'Unnecessary conditional, value is always truthy.', + alwaysTruthyFunc: + 'This callback should return a conditional, but return is always truthy.', + literalBooleanExpression: + 'Unnecessary conditional, both sides of the expression are literal values.', + never: 'Unnecessary conditional, value is `never`.', + neverNullish: + 'Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined.', + neverOptionalChain: 'Unnecessary optional chain on a non-nullish value.', + noOverlapBooleanExpression: + 'Unnecessary conditional, the types have no overlap.', + noStrictNullCheck: + 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', + replaceWithTrue: 'Replace always true expression with `true`.', + typeGuardAlreadyIsType: + 'Unnecessary conditional, expression already has the type being checked by the {{typeGuardOrAssertionFunction}}.', + }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowConstantLoopConditions: { + type: 'boolean', description: 'Whether to ignore constant loop conditions, such as `while (true)`.', - type: 'boolean', }, allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: { + type: 'boolean', description: 'Whether to not error when running with a tsconfig that has strictNullChecks turned.', - type: 'boolean', }, checkTypePredicates: { + type: 'boolean', description: 'Whether to check the asserted argument of a type predicate function for unnecessary conditions', - type: 'boolean', }, }, - additionalProperties: false, }, ], - fixable: 'code', - messages: { - alwaysTruthy: 'Unnecessary conditional, value is always truthy.', - alwaysFalsy: 'Unnecessary conditional, value is always falsy.', - alwaysTruthyFunc: - 'This callback should return a conditional, but return is always truthy.', - alwaysFalsyFunc: - 'This callback should return a conditional, but return is always falsy.', - neverNullish: - 'Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined.', - alwaysNullish: - 'Unnecessary conditional, left-hand side of `??` operator is always `null` or `undefined`.', - literalBooleanExpression: - 'Unnecessary conditional, both sides of the expression are literal values.', - replaceWithTrue: 'Replace always true expression with `true`.', - noOverlapBooleanExpression: - 'Unnecessary conditional, the types have no overlap.', - never: 'Unnecessary conditional, value is `never`.', - neverOptionalChain: 'Unnecessary optional chain on a non-nullish value.', - noStrictNullCheck: - 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', - typeGuardAlreadyIsType: - 'Unnecessary conditional, expression already has the type being checked by the {{typeGuardOrAssertionFunction}}.', - }, }, defaultOptions: [ { @@ -184,8 +185,8 @@ export default createRule({ ) { context.report({ loc: { - start: { line: 0, column: 0 }, - end: { line: 0, column: 0 }, + start: { column: 0, line: 0 }, + end: { column: 0, line: 0 }, }, messageId: 'noStrictNullCheck', }); @@ -693,7 +694,7 @@ export default createRule({ function checkOptionalChain( node: TSESTree.CallExpression | TSESTree.MemberExpression, beforeOperator: TSESTree.Node, - fix: '.' | '', + fix: '' | '.', ): void { // We only care if this step in the chain is optional. If just descend // from an optional chain, then that's fine. @@ -725,8 +726,8 @@ export default createRule({ ); context.report({ - node, loc: questionDotOperator.loc, + node, messageId: 'neverOptionalChain', fix(fixer) { return fixer.replaceText(questionDotOperator, fix); @@ -749,7 +750,7 @@ export default createRule({ ): void { // Similar to checkLogicalExpressionForUnnecessaryConditionals, since // a ||= b is equivalent to a || (a = b) - if (['||=', '&&='].includes(node.operator)) { + if (['&&=', '||='].includes(node.operator)) { checkNode(node.left); } else if (node.operator === '??=') { checkNodeForNullish(node.left); @@ -770,12 +771,14 @@ export default createRule({ } }, CallExpression: checkCallExpression, + 'CallExpression[optional = true]': checkOptionalCallExpression, ConditionalExpression: (node): void => checkNode(node.test), DoWhileStatement: checkIfLoopIsNecessaryConditional, ForStatement: checkIfLoopIsNecessaryConditional, IfStatement: (node): void => checkNode(node.test), LogicalExpression: checkLogicalExpressionForUnnecessaryConditionals, - SwitchCase({ test, parent }): void { + 'MemberExpression[optional = true]': checkOptionalMemberExpression, + SwitchCase({ parent, test }): void { // only check `case ...:`, not `default:` if (test) { checkIfBoolExpressionIsNecessaryConditional( @@ -787,8 +790,6 @@ export default createRule({ } }, WhileStatement: checkIfLoopIsNecessaryConditional, - 'MemberExpression[optional = true]': checkOptionalMemberExpression, - 'CallExpression[optional = true]': checkOptionalCallExpression, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-parameter-property-assignment.ts b/packages/eslint-plugin/src/rules/no-unnecessary-parameter-property-assignment.ts index 77743ff31519..3024d4fa0783 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-parameter-property-assignment.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-parameter-property-assignment.ts @@ -1,14 +1,16 @@ -import { DefinitionType } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; + +import { DefinitionType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import { createRule, getStaticStringValue, nullThrows } from '../util'; -const UNNECESSARY_OPERATORS = new Set(['=', '&&=', '||=', '??=']); +const UNNECESSARY_OPERATORS = new Set(['??=', '&&=', '=', '||=']); export default createRule({ name: 'no-unnecessary-parameter-property-assignment', meta: { + type: 'suggestion', docs: { description: 'Disallow unnecessary assignment of constructor property parameter', @@ -18,13 +20,12 @@ export default createRule({ 'This assignment is unnecessary since it is already assigned by a parameter property.', }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { const reportInfoStack: { - assignedBeforeUnnecessary: Set; assignedBeforeConstructor: Set; + assignedBeforeUnnecessary: Set; unnecessaryAssignments: { name: string; node: TSESTree.AssignmentExpression; @@ -57,9 +58,9 @@ export default createRule({ function findParentFunction( node: TSESTree.Node | undefined, ): - | TSESTree.FunctionExpression - | TSESTree.FunctionDeclaration | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression | undefined { if ( !node || @@ -136,13 +137,13 @@ export default createRule({ return { ClassBody(): void { reportInfoStack.push({ - unnecessaryAssignments: [], - assignedBeforeUnnecessary: new Set(), assignedBeforeConstructor: new Set(), + assignedBeforeUnnecessary: new Set(), + unnecessaryAssignments: [], }); }, 'ClassBody:exit'(): void { - const { unnecessaryAssignments, assignedBeforeConstructor } = + const { assignedBeforeConstructor, unnecessaryAssignments } = nullThrows(reportInfoStack.pop(), 'The top stack should exist'); unnecessaryAssignments.forEach(({ name, node }) => { if (assignedBeforeConstructor.has(name)) { @@ -154,32 +155,6 @@ export default createRule({ }); }); }, - 'PropertyDefinition AssignmentExpression'( - node: TSESTree.AssignmentExpression, - ): void { - const name = getPropertyName(node.left); - - if (!name) { - return; - } - - const functionNode = findParentFunction(node); - if ( - functionNode && - !( - isArrowIIFE(functionNode) && - findParentPropertyDefinition(node)?.value === functionNode.parent - ) - ) { - return; - } - - const { assignedBeforeConstructor } = nullThrows( - reportInfoStack.at(-1), - 'The top stack should exist', - ); - assignedBeforeConstructor.add(name); - }, "MethodDefinition[kind='constructor'] > FunctionExpression AssignmentExpression"( node: TSESTree.AssignmentExpression, ): void { @@ -226,6 +201,32 @@ export default createRule({ }); } }, + 'PropertyDefinition AssignmentExpression'( + node: TSESTree.AssignmentExpression, + ): void { + const name = getPropertyName(node.left); + + if (!name) { + return; + } + + const functionNode = findParentFunction(node); + if ( + functionNode && + !( + isArrowIIFE(functionNode) && + findParentPropertyDefinition(node)?.value === functionNode.parent + ) + ) { + return; + } + + const { assignedBeforeConstructor } = nullThrows( + reportInfoStack.at(-1), + 'The top stack should exist', + ); + assignedBeforeConstructor.add(name); + }, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts index 434df4f56a32..c8d8e893a383 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-qualifier.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -8,6 +9,7 @@ import { createRule, getParserServices } from '../util'; export default createRule({ name: 'no-unnecessary-qualifier', meta: { + type: 'suggestion', docs: { description: 'Disallow unnecessary namespace qualifiers', requiresTypeChecking: true, @@ -18,7 +20,6 @@ export default createRule({ "Qualifier is unnecessary since '{{ name }}' is in scope.", }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { @@ -157,25 +158,15 @@ export default createRule({ } return { - 'TSModuleDeclaration > TSModuleBlock'( - node: TSESTree.TSModuleBlock, - ): void { - enterDeclaration(node.parent); - }, - TSEnumDeclaration: enterDeclaration, - 'ExportNamedDeclaration[declaration.type="TSModuleDeclaration"]': - enterDeclaration, 'ExportNamedDeclaration[declaration.type="TSEnumDeclaration"]': enterDeclaration, - 'TSModuleDeclaration:exit': exitDeclaration, - 'TSEnumDeclaration:exit': exitDeclaration, - 'ExportNamedDeclaration[declaration.type="TSModuleDeclaration"]:exit': - exitDeclaration, 'ExportNamedDeclaration[declaration.type="TSEnumDeclaration"]:exit': exitDeclaration, - TSQualifiedName(node: TSESTree.TSQualifiedName): void { - visitNamespaceAccess(node, node.left, node.right); - }, + 'ExportNamedDeclaration[declaration.type="TSModuleDeclaration"]': + enterDeclaration, + 'ExportNamedDeclaration[declaration.type="TSModuleDeclaration"]:exit': + exitDeclaration, + 'MemberExpression:exit': resetCurrentNamespaceExpression, 'MemberExpression[computed=false]'( node: TSESTree.MemberExpression, ): void { @@ -184,8 +175,18 @@ export default createRule({ visitNamespaceAccess(node, node.object, property); } }, + TSEnumDeclaration: enterDeclaration, + 'TSEnumDeclaration:exit': exitDeclaration, + 'TSModuleDeclaration:exit': exitDeclaration, + 'TSModuleDeclaration > TSModuleBlock'( + node: TSESTree.TSModuleBlock, + ): void { + enterDeclaration(node.parent); + }, + TSQualifiedName(node: TSESTree.TSQualifiedName): void { + visitNamespaceAccess(node, node.left, node.right); + }, 'TSQualifiedName:exit': resetCurrentNamespaceExpression, - 'MemberExpression:exit': resetCurrentNamespaceExpression, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts b/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts index 212e44dfdadb..9c801cefccaa 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-template-expression.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -28,13 +29,13 @@ function endsWithUnescapedDollarSign(str: string): boolean { export default createRule<[], MessageId>({ name: 'no-unnecessary-template-expression', meta: { - fixable: 'code', type: 'suggestion', docs: { description: 'Disallow unnecessary template expressions', recommended: 'strict', requiresTypeChecking: true, }, + fixable: 'code', messages: { noUnnecessaryTemplateExpression: 'Template literal expression is unnecessary and can be simplified.', @@ -47,7 +48,7 @@ export default createRule<[], MessageId>({ function isUnderlyingTypeString( expression: TSESTree.Expression, - ): expression is TSESTree.StringLiteral | TSESTree.Identifier { + ): expression is TSESTree.Identifier | TSESTree.StringLiteral { const type = getConstrainedTypeAtLocation(services, expression); const isString = (t: ts.Type): boolean => { @@ -113,9 +114,9 @@ export default createRule<[], MessageId>({ messageId: 'noUnnecessaryTemplateExpression', fix(fixer): TSESLint.RuleFix | null { const wrappingCode = getMovedNodeCode({ - sourceCode: context.sourceCode, - nodeToMove: node.expressions[0], destinationNode: node, + nodeToMove: node.expressions[0], + sourceCode: context.sourceCode, }); return fixer.replaceText(node, wrappingCode); diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts index d482ef1b672a..a41afacf62a4 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-arguments.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -25,6 +26,7 @@ type MessageIds = 'unnecessaryTypeParameter'; export default createRule<[], MessageIds>({ name: 'no-unnecessary-type-arguments', meta: { + type: 'suggestion', docs: { description: 'Disallow type arguments that are equal to the default', recommended: 'strict', @@ -36,7 +38,6 @@ export default createRule<[], MessageIds>({ 'This is the default value for this type parameter, so it can be omitted.', }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { 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 0d3d4122a859..b2c4697d74f8 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -1,7 +1,8 @@ import type { Scope } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import type { ReportFixFunction } from '@typescript-eslint/utils/ts-eslint'; + +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -28,6 +29,7 @@ type MessageIds = 'contextuallyUnnecessary' | 'unnecessaryAssertion'; export default createRule({ name: 'no-unnecessary-type-assertion', meta: { + type: 'suggestion', docs: { description: 'Disallow type assertions that do not change the type of an expression', @@ -36,10 +38,10 @@ export default createRule({ }, fixable: 'code', messages: { - unnecessaryAssertion: - 'This assertion is unnecessary since it does not change the type of the expression.', contextuallyUnnecessary: 'This assertion is unnecessary since the receiver accepts the original type of the expression.', + unnecessaryAssertion: + 'This assertion is unnecessary since it does not change the type of the expression.', }, schema: [ { @@ -47,8 +49,8 @@ export default createRule({ additionalProperties: false, properties: { typesToIgnore: { - description: 'A list of type names to ignore.', type: 'array', + description: 'A list of type names to ignore.', items: { type: 'string', }, @@ -56,7 +58,6 @@ export default createRule({ }, }, ], - type: 'suggestion', }, defaultOptions: [{}], create(context, [options]) { @@ -208,6 +209,83 @@ export default createRule({ } return { + 'TSAsExpression, TSTypeAssertion'( + node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion, + ): void { + if ( + options.typesToIgnore?.includes( + context.sourceCode.getText(node.typeAnnotation), + ) + ) { + return; + } + + const castType = services.getTypeAtLocation(node); + const uncastType = services.getTypeAtLocation(node.expression); + const typeIsUnchanged = isTypeUnchanged(uncastType, castType); + + const wouldSameTypeBeInferred = castType.isLiteral() + ? isImplicitlyNarrowedConstDeclaration(node) + : !isConstAssertion(node.typeAnnotation); + + if (typeIsUnchanged && wouldSameTypeBeInferred) { + context.report({ + node, + messageId: 'unnecessaryAssertion', + fix(fixer) { + if (node.type === AST_NODE_TYPES.TSTypeAssertion) { + const openingAngleBracket = nullThrows( + context.sourceCode.getTokenBefore( + node.typeAnnotation, + token => + token.type === AST_TOKEN_TYPES.Punctuator && + token.value === '<', + ), + NullThrowsReasons.MissingToken('<', 'type annotation'), + ); + const closingAngleBracket = nullThrows( + context.sourceCode.getTokenAfter( + node.typeAnnotation, + token => + token.type === AST_TOKEN_TYPES.Punctuator && + token.value === '>', + ), + NullThrowsReasons.MissingToken('>', 'type annotation'), + ); + + // < ( number ) > ( 3 + 5 ) + // ^---remove---^ + return fixer.removeRange([ + openingAngleBracket.range[0], + closingAngleBracket.range[1], + ]); + } + // `as` is always present in TSAsExpression + const asToken = nullThrows( + context.sourceCode.getTokenAfter( + node.expression, + token => + token.type === AST_TOKEN_TYPES.Identifier && + token.value === 'as', + ), + NullThrowsReasons.MissingToken('>', 'type annotation'), + ); + const tokenBeforeAs = nullThrows( + context.sourceCode.getTokenBefore(asToken, { + includeComments: true, + }), + NullThrowsReasons.MissingToken('comment', 'as'), + ); + + // ( 3 + 5 ) as number + // ^--remove--^ + return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]); + }, + }); + } + + // TODO - add contextually unnecessary check for this + }, TSNonNullExpression(node): void { const removeExclamationFix: ReportFixFunction = fixer => { const exclamationToken = nullThrows( @@ -306,83 +384,6 @@ export default createRule({ } } }, - 'TSAsExpression, TSTypeAssertion'( - node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion, - ): void { - if ( - options.typesToIgnore?.includes( - context.sourceCode.getText(node.typeAnnotation), - ) - ) { - return; - } - - const castType = services.getTypeAtLocation(node); - const uncastType = services.getTypeAtLocation(node.expression); - const typeIsUnchanged = isTypeUnchanged(uncastType, castType); - - const wouldSameTypeBeInferred = castType.isLiteral() - ? isImplicitlyNarrowedConstDeclaration(node) - : !isConstAssertion(node.typeAnnotation); - - if (typeIsUnchanged && wouldSameTypeBeInferred) { - context.report({ - node, - messageId: 'unnecessaryAssertion', - fix(fixer) { - if (node.type === AST_NODE_TYPES.TSTypeAssertion) { - const openingAngleBracket = nullThrows( - context.sourceCode.getTokenBefore( - node.typeAnnotation, - token => - token.type === AST_TOKEN_TYPES.Punctuator && - token.value === '<', - ), - NullThrowsReasons.MissingToken('<', 'type annotation'), - ); - const closingAngleBracket = nullThrows( - context.sourceCode.getTokenAfter( - node.typeAnnotation, - token => - token.type === AST_TOKEN_TYPES.Punctuator && - token.value === '>', - ), - NullThrowsReasons.MissingToken('>', 'type annotation'), - ); - - // < ( number ) > ( 3 + 5 ) - // ^---remove---^ - return fixer.removeRange([ - openingAngleBracket.range[0], - closingAngleBracket.range[1], - ]); - } - // `as` is always present in TSAsExpression - const asToken = nullThrows( - context.sourceCode.getTokenAfter( - node.expression, - token => - token.type === AST_TOKEN_TYPES.Identifier && - token.value === 'as', - ), - NullThrowsReasons.MissingToken('>', 'type annotation'), - ); - const tokenBeforeAs = nullThrows( - context.sourceCode.getTokenBefore(asToken, { - includeComments: true, - }), - NullThrowsReasons.MissingToken('comment', 'as'), - ); - - // ( 3 + 5 ) as number - // ^--remove--^ - return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]); - }, - }); - } - - // TODO - add contextually unnecessary check for this - }, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts index d31044ce86d2..493e93e96877 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts @@ -1,10 +1,11 @@ -import { extname } from 'node:path'; - import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import { extname } from 'node:path'; import * as ts from 'typescript'; import type { MakeRequired } from '../util'; + import { createRule } from '../util'; type TypeParameterWithConstraint = MakeRequired< @@ -15,19 +16,19 @@ type TypeParameterWithConstraint = MakeRequired< export default createRule({ name: 'no-unnecessary-type-constraint', meta: { + type: 'suggestion', docs: { description: 'Disallow unnecessary constraints on generic types', recommended: 'recommended', }, hasSuggestions: true, messages: { - unnecessaryConstraint: - 'Constraining the generic type `{{name}}` to `{{constraint}}` does nothing and is unnecessary.', removeUnnecessaryConstraint: 'Remove the unnecessary `{{constraint}}` constraint.', + unnecessaryConstraint: + 'Constraining the generic type `{{name}}` to `{{constraint}}` does nothing and is unnecessary.', }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { @@ -77,9 +78,11 @@ export default createRule({ if (constraint) { context.report({ + node, + messageId: 'unnecessaryConstraint', data: { - constraint, name: node.name.name, + constraint, }, suggest: [ { @@ -95,8 +98,6 @@ export default createRule({ }, }, ], - messageId: 'unnecessaryConstraint', - node, }); } }; diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-parameters.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-parameters.ts index fef612065081..e427a17f29e9 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-parameters.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-parameters.ts @@ -1,32 +1,34 @@ import type { Reference } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; import type { MakeRequired } from '../util'; + import { createRule, getParserServices, nullThrows } from '../util'; type NodeWithTypeParameters = MakeRequired< - ts.SignatureDeclaration | ts.ClassLikeDeclaration, + ts.ClassLikeDeclaration | ts.SignatureDeclaration, 'typeParameters' >; export default createRule({ - defaultOptions: [], + name: 'no-unnecessary-type-parameters', meta: { + type: 'problem', docs: { description: "Disallow type parameters that aren't used multiple times", - requiresTypeChecking: true, recommended: 'strict', + requiresTypeChecking: true, }, messages: { sole: 'Type parameter {{name}} is {{uses}} in the {{descriptor}} signature.', }, schema: [], - type: 'problem', }, - name: 'no-unnecessary-type-parameters', + defaultOptions: [], create(context) { const parserServices = getParserServices(context); @@ -50,11 +52,7 @@ export default createRule({ const smTypeParameterVariable = nullThrows( (() => { const variable = scope.set.get(esTypeParameter.name.name); - return variable != null && - variable.isTypeVariable && - !variable.isValueVariable - ? variable - : undefined; + return variable?.isTypeVariable ? variable : undefined; })(), "Type parameter should be present in scope's variables.", ); @@ -79,13 +77,13 @@ export default createRule({ } context.report({ + node: esTypeParameter, + messageId: 'sole', data: { + name: typeParameter.name.text, descriptor, uses: identifierCounts === 1 ? 'never used' : 'used only once', - name: typeParameter.name.text, }, - node: esTypeParameter, - messageId: 'sole', }); } } @@ -413,9 +411,9 @@ function collectTypeParameterUsageCounts( } interface MappedType extends ts.ObjectType { - typeParameter?: ts.Type; constraintType?: ts.Type; templateType?: ts.Type; + typeParameter?: ts.Type; } function isMappedType(type: ts.Type): type is MappedType { diff --git a/packages/eslint-plugin/src/rules/no-unsafe-argument.ts b/packages/eslint-plugin/src/rules/no-unsafe-argument.ts index 4617446bac38..f89a7ddcec88 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-argument.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-argument.ts @@ -1,7 +1,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; +import type * as ts from 'typescript'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; -import type * as ts from 'typescript'; import { createRule, @@ -26,24 +27,31 @@ const enum RestTypeKind { } type RestType = | { - type: ts.Type; - kind: RestTypeKind.Array; index: number; + kind: RestTypeKind.Array; + type: ts.Type; } | { - type: ts.Type; - kind: RestTypeKind.Other; index: number; + kind: RestTypeKind.Other; + type: ts.Type; } | { - typeArguments: readonly ts.Type[]; - kind: RestTypeKind.Tuple; index: number; + kind: RestTypeKind.Tuple; + typeArguments: readonly ts.Type[]; }; class FunctionSignature { + private hasConsumedArguments = false; + private parameterTypeIndex = 0; + private constructor( + private paramTypes: ts.Type[], + private restType: RestType | null, + ) {} + public static create( checker: ts.TypeChecker, tsNode: ts.CallLikeExpression, @@ -67,20 +75,20 @@ class FunctionSignature { if (checker.isArrayType(type)) { restType = { type: checker.getTypeArguments(type)[0], - kind: RestTypeKind.Array, index: i, + kind: RestTypeKind.Array, }; } else if (checker.isTupleType(type)) { restType = { - typeArguments: checker.getTypeArguments(type), - kind: RestTypeKind.Tuple, index: i, + kind: RestTypeKind.Tuple, + typeArguments: checker.getTypeArguments(type), }; } else { restType = { type, - kind: RestTypeKind.Other, index: i, + kind: RestTypeKind.Other, }; } break; @@ -92,12 +100,9 @@ class FunctionSignature { return new this(paramTypes, restType); } - private hasConsumedArguments = false; - - private constructor( - private paramTypes: ts.Type[], - private restType: RestType | null, - ) {} + public consumeRemainingArguments(): void { + this.hasConsumedArguments = true; + } public getNextParameterType(): ts.Type | null { const index = this.parameterTypeIndex; @@ -134,10 +139,6 @@ class FunctionSignature { } return this.paramTypes[index]; } - - public consumeRemainingArguments(): void { - this.hasConsumedArguments = true; - } } export default createRule<[], MessageIds>({ @@ -152,10 +153,10 @@ export default createRule<[], MessageIds>({ messages: { unsafeArgument: 'Unsafe argument of type {{sender}} assigned to a parameter of type {{receiver}}.', - unsafeTupleSpread: - 'Unsafe spread of a tuple type. The argument is {{sender}} and is assigned to a parameter of type {{receiver}}.', unsafeArraySpread: 'Unsafe spread of an {{sender}} array type.', unsafeSpread: 'Unsafe spread of an {{sender}} type.', + unsafeTupleSpread: + 'Unsafe spread of a tuple type. The argument is {{sender}} and is assigned to a parameter of type {{receiver}}.', }, schema: [], }, @@ -192,7 +193,7 @@ export default createRule<[], MessageIds>({ } function checkUnsafeArguments( - args: TSESTree.Expression[] | TSESTree.CallExpressionArgument[], + args: TSESTree.CallExpressionArgument[] | TSESTree.Expression[], callee: TSESTree.Expression, node: | TSESTree.CallExpression @@ -228,18 +229,18 @@ export default createRule<[], MessageIds>({ if (isTypeAnyType(spreadArgType)) { // foo(...any) context.report({ - data: { sender: describeType(spreadArgType) }, node: argument, messageId: 'unsafeSpread', + data: { sender: describeType(spreadArgType) }, }); } else if (isTypeAnyArrayType(spreadArgType, checker)) { // foo(...any[]) // TODO - we could break down the spread and compare the array type against each argument context.report({ - data: { sender: describeTypeForSpread(spreadArgType) }, node: argument, messageId: 'unsafeArraySpread', + data: { sender: describeTypeForSpread(spreadArgType) }, }); } else if (checker.isTupleType(spreadArgType)) { // foo(...[tuple1, tuple2]) @@ -263,8 +264,8 @@ export default createRule<[], MessageIds>({ node: argument, messageId: 'unsafeTupleSpread', data: { - sender: describeTypeForTuple(tupleType), receiver: describeType(parameterType), + sender: describeTypeForTuple(tupleType), }, }); } @@ -300,8 +301,8 @@ export default createRule<[], MessageIds>({ node: argument, messageId: 'unsafeArgument', data: { - sender: describeType(argumentType), receiver: describeType(parameterType), + sender: describeType(argumentType), }, }); } diff --git a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts index 22b3bfd09bd9..a9f1358c0c6d 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts @@ -1,7 +1,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; +import type * as ts from 'typescript'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; -import type * as ts from 'typescript'; import { createRule, @@ -46,9 +47,9 @@ export default createRule({ 'Unsafe array destructuring of an {{sender}} array value.', unsafeArrayPatternFromTuple: 'Unsafe array destructuring of a tuple element with an {{sender}} value.', + unsafeArraySpread: 'Unsafe spread of an {{sender}} value in an array.', unsafeAssignment: 'Unsafe assignment of type {{sender}} to a variable of type {{receiver}}.', - unsafeArraySpread: 'Unsafe spread of an {{sender}} value in an array.', }, schema: [], }, @@ -299,7 +300,7 @@ export default createRule({ return false; } - const { sender, receiver } = result; + const { receiver, sender } = result; context.report({ node: reportingNode, messageId: 'unsafeAssignment', @@ -324,8 +325,8 @@ export default createRule({ ): Readonly> | undefined { if (receiverType) { return { - sender: `\`${checker.typeToString(senderType)}\``, receiver: `\`${checker.typeToString(receiverType)}\``, + sender: `\`${checker.typeToString(senderType)}\``, }; } return { @@ -336,29 +337,26 @@ export default createRule({ } return { - 'VariableDeclarator[init != null]'( - node: TSESTree.VariableDeclarator, + 'AssignmentExpression[operator = "="], AssignmentPattern'( + node: TSESTree.AssignmentExpression | TSESTree.AssignmentPattern, ): void { - const init = nullThrows( - node.init, - NullThrowsReasons.MissingToken(node.type, 'init'), - ); let didReport = checkAssignment( - node.id, - init, + node.left, + node.right, node, - getComparisonType(node.id.typeAnnotation), + // the variable already has some form of a type to compare against + ComparisonType.Basic, ); if (!didReport) { - didReport = checkArrayDestructureHelper(node.id, init); + didReport = checkArrayDestructureHelper(node.left, node.right); } if (!didReport) { - checkObjectDestructureHelper(node.id, init); + checkObjectDestructureHelper(node.left, node.right); } }, 'PropertyDefinition[value != null]'( - node: TSESTree.PropertyDefinition & { value: NonNullable }, + node: { value: NonNullable } & TSESTree.PropertyDefinition, ): void { checkAssignment( node.key, @@ -367,22 +365,25 @@ export default createRule({ getComparisonType(node.typeAnnotation), ); }, - 'AssignmentExpression[operator = "="], AssignmentPattern'( - node: TSESTree.AssignmentExpression | TSESTree.AssignmentPattern, + 'VariableDeclarator[init != null]'( + node: TSESTree.VariableDeclarator, ): void { + const init = nullThrows( + node.init, + NullThrowsReasons.MissingToken(node.type, 'init'), + ); let didReport = checkAssignment( - node.left, - node.right, + node.id, + init, node, - // the variable already has some form of a type to compare against - ComparisonType.Basic, + getComparisonType(node.id.typeAnnotation), ); if (!didReport) { - didReport = checkArrayDestructureHelper(node.left, node.right); + didReport = checkArrayDestructureHelper(node.id, init); } if (!didReport) { - checkObjectDestructureHelper(node.left, node.right); + checkObjectDestructureHelper(node.id, init); } }, // object pattern props are checked via assignments diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 8149f2736a15..2c29caa2c9e1 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import * as tsutils from 'ts-api-utils'; import { diff --git a/packages/eslint-plugin/src/rules/no-unsafe-declaration-merging.ts b/packages/eslint-plugin/src/rules/no-unsafe-declaration-merging.ts index 2d8a797b116f..3a605e81ea53 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-declaration-merging.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-declaration-merging.ts @@ -1,5 +1,6 @@ import type { Scope } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; diff --git a/packages/eslint-plugin/src/rules/no-unsafe-enum-comparison.ts b/packages/eslint-plugin/src/rules/no-unsafe-enum-comparison.ts index ae7b33bc0e3e..fe2106fae9d7 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-enum-comparison.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-enum-comparison.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -57,13 +58,13 @@ function getEnumValueType(type: ts.Type): ts.TypeFlags | undefined { export default createRule({ name: 'no-unsafe-enum-comparison', meta: { - hasSuggestions: true, type: 'suggestion', docs: { description: 'Disallow comparing an enum value with a non-enum value', recommended: 'recommended', requiresTypeChecking: true, }, + hasSuggestions: true, messages: { mismatchedCase: 'The case statement does not have a shared enum type with the switch predicate.', @@ -141,8 +142,8 @@ export default createRule({ if (isMismatchedComparison(leftType, rightType)) { context.report({ - messageId: 'mismatchedCondition', node, + messageId: 'mismatchedCondition', suggest: [ { messageId: 'replaceValueWithEnum', @@ -197,8 +198,8 @@ export default createRule({ if (isMismatchedComparison(leftType, rightType)) { context.report({ - messageId: 'mismatchedCase', node, + messageId: 'mismatchedCase', }); } }, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-function-type.ts b/packages/eslint-plugin/src/rules/no-unsafe-function-type.ts index 624c038f8ff7..4d0cee981ec6 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-function-type.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-function-type.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isReferenceToGlobalFunction } from '../util'; diff --git a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts index 4fae2b560073..6cf16d5211e4 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts @@ -1,7 +1,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; +import type * as ts from 'typescript'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; -import type * as ts from 'typescript'; import { createRule, @@ -16,7 +17,7 @@ const enum State { Safe = 2, } -function createDataType(type: ts.Type): '`error` typed' | '`any`' { +function createDataType(type: ts.Type): '`any`' | '`error` typed' { const isErrorType = tsutils.isIntrinsicErrorType(type); return isErrorType ? '`error` typed' : '`any`'; } @@ -31,14 +32,14 @@ export default createRule({ requiresTypeChecking: true, }, messages: { + unsafeComputedMemberAccess: + 'Computed name {{property}} resolves to an {{type}} value.', unsafeMemberExpression: 'Unsafe member access {{property}} on an {{type}} value.', unsafeThisMemberExpression: [ 'Unsafe member access {{property}} on an `any` value. `this` is typed as `any`.', 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', ].join('\n'), - unsafeComputedMemberAccess: - 'Computed name {{property}} resolves to an {{type}} value.', }, schema: [], }, @@ -97,8 +98,8 @@ export default createRule({ node: node.property, messageId, data: { - property: node.computed ? `[${propertyName}]` : `.${propertyName}`, type: createDataType(type), + property: node.computed ? `[${propertyName}]` : `.${propertyName}`, }, }); } @@ -133,8 +134,8 @@ export default createRule({ node, messageId: 'unsafeComputedMemberAccess', data: { - property: `[${propertyName}]`, type: createDataType(type), + property: `[${propertyName}]`, }, }); } diff --git a/packages/eslint-plugin/src/rules/no-unsafe-return.ts b/packages/eslint-plugin/src/rules/no-unsafe-return.ts index 1b89ffbf8568..b5becaa4e633 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-return.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-return.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -29,12 +30,12 @@ export default createRule({ }, messages: { unsafeReturn: 'Unsafe return of a value of type {{type}}.', + unsafeReturnAssignment: + 'Unsafe return of type `{{sender}}` from function with return type `{{receiver}}`.', unsafeReturnThis: [ 'Unsafe return of a value of type `{{type}}`. `this` is typed as `any`.', 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', ].join('\n'), - unsafeReturnAssignment: - 'Unsafe return of type `{{sender}}` from function with return type `{{receiver}}`.', }, schema: [], }, @@ -219,19 +220,20 @@ export default createRule({ return; } - const { sender, receiver } = result; + const { receiver, sender } = result; return context.report({ node: reportingNode, messageId: 'unsafeReturnAssignment', data: { - sender: checker.typeToString(sender), receiver: checker.typeToString(receiver), + sender: checker.typeToString(sender), }, }); } } return { + 'ArrowFunctionExpression > :not(BlockStatement).body': checkReturn, ReturnStatement(node): void { const argument = node.argument; if (!argument) { @@ -240,7 +242,6 @@ export default createRule({ checkReturn(argument, node); }, - 'ArrowFunctionExpression > :not(BlockStatement).body': checkReturn, }; }, }); diff --git a/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts b/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts index a61572515437..fcfca3145db6 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-unary-minus.ts @@ -49,8 +49,8 @@ export default util.createRule({ ) ) { context.report({ - messageId: 'unaryMinus', node, + messageId: 'unaryMinus', data: { type: checker.typeToString(argType) }, }); } diff --git a/packages/eslint-plugin/src/rules/no-unused-expressions.ts b/packages/eslint-plugin/src/rules/no-unused-expressions.ts index 3c386aa69a87..894e1e435033 100644 --- a/packages/eslint-plugin/src/rules/no-unused-expressions.ts +++ b/packages/eslint-plugin/src/rules/no-unused-expressions.ts @@ -4,6 +4,7 @@ import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -22,14 +23,14 @@ export default createRule({ recommended: 'recommended', }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, messages: baseRule.meta.messages, + schema: baseRule.meta.schema, }, defaultOptions: [ { allowShortCircuit: false, - allowTernary: false, allowTaggedTemplates: false, + allowTernary: false, }, ], create(context, [{ allowShortCircuit = false, allowTernary = false }]) { diff --git a/packages/eslint-plugin/src/rules/no-unused-vars.ts b/packages/eslint-plugin/src/rules/no-unused-vars.ts index 417467a1faef..3b9cbda2ba04 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -2,11 +2,12 @@ import type { Definition, ScopeVariable, } from '@typescript-eslint/scope-manager'; +import type { TSESTree } from '@typescript-eslint/utils'; + import { DefinitionType, PatternVisitor, } from '@typescript-eslint/scope-manager'; -import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES, TSESLint } from '@typescript-eslint/utils'; import { @@ -25,30 +26,30 @@ export type Options = [ | 'all' | 'local' | { - vars?: 'all' | 'local'; - varsIgnorePattern?: string; args?: 'after-used' | 'all' | 'none'; - ignoreRestSiblings?: boolean; argsIgnorePattern?: string; caughtErrors?: 'all' | 'none'; caughtErrorsIgnorePattern?: string; destructuredArrayIgnorePattern?: string; ignoreClassWithStaticInitBlock?: boolean; + ignoreRestSiblings?: boolean; reportUsedIgnorePattern?: boolean; + vars?: 'all' | 'local'; + varsIgnorePattern?: string; }, ]; interface TranslatedOptions { - vars: 'all' | 'local'; - varsIgnorePattern?: RegExp; args: 'after-used' | 'all' | 'none'; - ignoreRestSiblings: boolean; argsIgnorePattern?: RegExp; caughtErrors: 'all' | 'none'; caughtErrorsIgnorePattern?: RegExp; destructuredArrayIgnorePattern?: RegExp; ignoreClassWithStaticInitBlock: boolean; + ignoreRestSiblings: boolean; reportUsedIgnorePattern: boolean; + vars: 'all' | 'local'; + varsIgnorePattern?: RegExp; } type VariableType = @@ -63,8 +64,15 @@ export default createRule({ type: 'problem', docs: { description: 'Disallow unused variables', - recommended: 'recommended', extendsBaseRule: true, + recommended: 'recommended', + }, + messages: { + unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.", + usedIgnoredVar: + "'{{varName}}' is marked as ignored but is used{{additional}}.", + usedOnlyAsType: + "'{{varName}}' is {{action}} but only used as a type{{additional}}.", }, schema: [ { @@ -75,71 +83,64 @@ export default createRule({ }, { type: 'object', + additionalProperties: false, properties: { - vars: { - description: - 'Whether to check all variables or only locally-declared variables.', - type: 'string', - enum: ['all', 'local'], - }, - varsIgnorePattern: { - description: - 'Regular expressions of variable names to not check for usage.', - type: 'string', - }, args: { - description: 'Whether to check all, some, or no arguments.', type: 'string', + description: 'Whether to check all, some, or no arguments.', enum: ['all', 'after-used', 'none'], }, argsIgnorePattern: { + type: 'string', description: 'Regular expressions of argument names to not check for usage.', - type: 'string', }, caughtErrors: { - description: 'Whether to check catch block arguments.', type: 'string', + description: 'Whether to check catch block arguments.', enum: ['all', 'none'], }, caughtErrorsIgnorePattern: { + type: 'string', description: 'Regular expressions of catch block argument names to not check for usage.', - type: 'string', }, destructuredArrayIgnorePattern: { + type: 'string', description: 'Regular expressions of destructured array variable names to not check for usage.', - type: 'string', }, ignoreClassWithStaticInitBlock: { + type: 'boolean', description: 'Whether to ignore classes with at least one static initialization block.', - type: 'boolean', }, ignoreRestSiblings: { + type: 'boolean', description: 'Whether to ignore sibling properties in `...` destructurings.', - type: 'boolean', }, reportUsedIgnorePattern: { + type: 'boolean', description: 'Whether to report variables that match any of the valid ignore pattern options if they have been used.', - type: 'boolean', + }, + vars: { + type: 'string', + description: + 'Whether to check all variables or only locally-declared variables.', + enum: ['all', 'local'], + }, + varsIgnorePattern: { + type: 'string', + description: + 'Regular expressions of variable names to not check for usage.', }, }, - additionalProperties: false, }, ], }, ], - messages: { - unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.", - usedIgnoredVar: - "'{{varName}}' is marked as ignored but is used{{additional}}.", - usedOnlyAsType: - "'{{varName}}' is {{action}} but only used as a type{{additional}}.", - }, }, defaultOptions: [{}], create(context, [firstOption]) { @@ -147,12 +148,12 @@ export default createRule({ const options = ((): TranslatedOptions => { const options: TranslatedOptions = { - vars: 'all', args: 'after-used', - ignoreRestSiblings: false, caughtErrors: 'all', ignoreClassWithStaticInitBlock: false, + ignoreRestSiblings: false, reportUsedIgnorePattern: false, + vars: 'all', }; if (typeof firstOption === 'string') { @@ -285,7 +286,7 @@ export default createRule({ let additionalMessageData = ''; if (def) { - const { variableDescription, pattern } = getVariableDescription( + const { pattern, variableDescription } = getVariableDescription( defToVariableType(def), ); @@ -295,9 +296,9 @@ export default createRule({ } return { - varName: unusedVar.name, action: 'defined', additional: additionalMessageData, + varName: unusedVar.name, }; } @@ -314,7 +315,7 @@ export default createRule({ let additionalMessageData = ''; if (def) { - const { variableDescription, pattern } = getVariableDescription( + const { pattern, variableDescription } = getVariableDescription( defToVariableType(def), ); @@ -324,9 +325,9 @@ export default createRule({ } return { - varName: unusedVar.name, action: 'assigned a value', additional: additionalMessageData, + varName: unusedVar.name, }; } @@ -341,7 +342,7 @@ export default createRule({ variable: ScopeVariable, variableType: VariableType, ): Record { - const { variableDescription, pattern } = + const { pattern, variableDescription } = getVariableDescription(variableType); let additionalMessageData = ''; @@ -351,8 +352,8 @@ export default createRule({ } return { - varName: variable.name, additional: additionalMessageData, + varName: variable.name, }; } @@ -521,7 +522,13 @@ export default createRule({ def.name.type === AST_NODE_TYPES.Identifier && options.varsIgnorePattern?.test(def.name.name) ) { - if (options.reportUsedIgnorePattern && used) { + if ( + options.reportUsedIgnorePattern && + used && + /* enum members are always marked as 'used' by `collectVariables`, but in reality they may be used or + unused. either way, don't complain about their naming. */ + def.type !== TSESLint.Scope.DefinitionType.TSEnumMember + ) { context.report({ node: def.name, messageId: 'usedIgnoredVar', @@ -628,8 +635,8 @@ export default createRule({ const loc = { start, end: { - line: start.line, column: start.column + idLength, + line: start.line, }, }; @@ -649,12 +656,12 @@ export default createRule({ const directiveComment = unusedVar.eslintExplicitGlobalComments[0]; context.report({ - node: programNode, loc: getNameLocationInGlobalDirectiveComment( context.sourceCode, directiveComment, unusedVar.name, ), + node: programNode, messageId: 'unusedVar', data: getDefinedMessageData(unusedVar), }); @@ -739,8 +746,8 @@ export default createRule({ let scope = context.sourceCode.getScope(node); const shouldUseUpperScope = [ - AST_NODE_TYPES.TSModuleDeclaration, AST_NODE_TYPES.TSDeclareFunction, + AST_NODE_TYPES.TSModuleDeclaration, ].includes(node.type); if (scope.variableScope !== scope) { diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index 4c6cc0041971..8350f6477318 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -1,5 +1,6 @@ -import { DefinitionType } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; + +import { DefinitionType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES, TSESLint } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -33,13 +34,13 @@ function parseOptions(options: Config | string | null): Required { } return { - functions, + allowNamedExports, classes, enums, - variables, - typedefs, + functions, ignoreTypeReferences, - allowNamedExports, + typedefs, + variables, }; } @@ -203,15 +204,15 @@ function isInInitializer( } interface Config { - functions?: boolean; + allowNamedExports?: boolean; classes?: boolean; enums?: boolean; - variables?: boolean; - typedefs?: boolean; + functions?: boolean; ignoreTypeReferences?: boolean; - allowNamedExports?: boolean; + typedefs?: boolean; + variables?: boolean; } -type Options = [Config | 'nofunc']; +type Options = ['nofunc' | Config]; type MessageIds = 'noUseBeforeDefine'; export default createRule({ @@ -234,37 +235,37 @@ export default createRule({ }, { type: 'object', + additionalProperties: false, properties: { - functions: { - description: - 'Whether to ignore references to function declarations.', - type: 'boolean', - }, + allowNamedExports: { type: 'boolean' }, classes: { + type: 'boolean', description: 'Whether to ignore references to class declarations.', - type: 'boolean', }, enums: { + type: 'boolean', description: 'Whether to check references to enums.', + }, + functions: { type: 'boolean', + description: + 'Whether to ignore references to function declarations.', }, - variables: { - description: 'Whether to ignore references to variables.', + ignoreTypeReferences: { type: 'boolean', + description: + 'Whether to ignore type references, such as in type annotations and assertions.', }, typedefs: { - description: 'Whether to check references to types.', type: 'boolean', + description: 'Whether to check references to types.', }, - ignoreTypeReferences: { - description: - 'Whether to ignore type references, such as in type annotations and assertions.', + variables: { type: 'boolean', + description: 'Whether to ignore references to variables.', }, - allowNamedExports: { type: 'boolean' }, }, - additionalProperties: false, }, ], }, @@ -272,13 +273,13 @@ export default createRule({ }, defaultOptions: [ { - functions: true, + allowNamedExports: false, classes: true, enums: true, - variables: true, - typedefs: true, + functions: true, ignoreTypeReferences: true, - allowNamedExports: false, + typedefs: true, + variables: true, }, ], create(context, optionsWithDefault) { diff --git a/packages/eslint-plugin/src/rules/no-useless-constructor.ts b/packages/eslint-plugin/src/rules/no-useless-constructor.ts index cd91a71c2002..9c45ec6534b0 100644 --- a/packages/eslint-plugin/src/rules/no-useless-constructor.ts +++ b/packages/eslint-plugin/src/rules/no-useless-constructor.ts @@ -1,10 +1,12 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; @@ -47,12 +49,12 @@ export default createRule({ type: 'problem', docs: { description: 'Disallow unnecessary constructors', - recommended: 'strict', extendsBaseRule: true, + recommended: 'strict', }, hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, messages: baseRule.meta.messages, + schema: baseRule.meta.schema, }, defaultOptions: [], create(context) { diff --git a/packages/eslint-plugin/src/rules/no-useless-empty-export.ts b/packages/eslint-plugin/src/rules/no-useless-empty-export.ts index a21f1cfa6224..70f3e7e8acae 100644 --- a/packages/eslint-plugin/src/rules/no-useless-empty-export.ts +++ b/packages/eslint-plugin/src/rules/no-useless-empty-export.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isDefinitionFile } from '../util'; @@ -26,6 +27,7 @@ const exportOrImportNodeTypes = new Set([ export default createRule({ name: 'no-useless-empty-export', meta: { + type: 'suggestion', docs: { description: "Disallow empty exports that don't change anything in a module file", @@ -36,7 +38,6 @@ export default createRule({ uselessExport: 'Empty export does nothing and can be removed.', }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { @@ -67,9 +68,9 @@ export default createRule({ if (foundOtherExport) { for (const emptyExport of emptyExports) { context.report({ - fix: fixer => fixer.remove(emptyExport), - messageId: 'uselessExport', node: emptyExport, + messageId: 'uselessExport', + fix: fixer => fixer.remove(emptyExport), }); } } diff --git a/packages/eslint-plugin/src/rules/no-var-requires.ts b/packages/eslint-plugin/src/rules/no-var-requires.ts index b7c38620168a..19fcd3795294 100644 --- a/packages/eslint-plugin/src/rules/no-var-requires.ts +++ b/packages/eslint-plugin/src/rules/no-var-requires.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import { createRule, getStaticStringValue } from '../util'; @@ -13,26 +14,26 @@ type MessageIds = 'noVarReqs'; export default createRule({ name: 'no-var-requires', meta: { - deprecated: true, - replacedBy: ['@typescript-eslint/no-require-imports'], type: 'problem', + deprecated: true, docs: { description: 'Disallow `require` statements except in import statements', }, messages: { noVarReqs: 'Require statement not part of import statement.', }, + replacedBy: ['@typescript-eslint/no-require-imports'], schema: [ { type: 'object', + additionalProperties: false, properties: { allow: { type: 'array', - items: { type: 'string' }, description: 'Patterns of import paths to allow requiring from.', + items: { type: 'string' }, }, }, - additionalProperties: false, }, ], }, diff --git a/packages/eslint-plugin/src/rules/no-wrapper-object-types.ts b/packages/eslint-plugin/src/rules/no-wrapper-object-types.ts index f51b6c8564b5..d587d1871c9b 100644 --- a/packages/eslint-plugin/src/rules/no-wrapper-object-types.ts +++ b/packages/eslint-plugin/src/rules/no-wrapper-object-types.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isReferenceToGlobalFunction } from '../util'; @@ -47,12 +48,12 @@ export default createRule({ const preferred = typeName.toLowerCase(); context.report({ - data: { typeName, preferred }, + node, + messageId: 'bannedClassType', + data: { preferred, typeName }, fix: includeFix ? (fixer): TSESLint.RuleFix => fixer.replaceText(node, preferred) : undefined, - messageId: 'bannedClassType', - node, }); } diff --git a/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts b/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts index e4b237cccc85..280d45f0d3f4 100644 --- a/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts +++ b/packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -13,6 +14,7 @@ import { export default createRule({ name: 'non-nullable-type-assertion-style', meta: { + type: 'suggestion', docs: { description: 'Enforce non-null assertions over explicit type casts', recommended: 'stylistic', @@ -24,7 +26,6 @@ export default createRule({ 'Use a ! assertion to more succinctly remove null and undefined from the type.', }, schema: [], - type: 'suggestion', }, defaultOptions: [], @@ -129,6 +130,8 @@ export default createRule({ ) > OperatorPrecedence.Unary; context.report({ + node, + messageId: 'preferNonNullAssertion', fix(fixer) { return fixer.replaceText( node, @@ -137,8 +140,6 @@ export default createRule({ : `(${expressionSourceCode})!`, ); }, - messageId: 'preferNonNullAssertion', - node, }); } }, diff --git a/packages/eslint-plugin/src/rules/only-throw-error.ts b/packages/eslint-plugin/src/rules/only-throw-error.ts index 1f0796fe98ca..0ebd6048bee3 100644 --- a/packages/eslint-plugin/src/rules/only-throw-error.ts +++ b/packages/eslint-plugin/src/rules/only-throw-error.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -25,32 +26,32 @@ export default createRule({ type: 'problem', docs: { description: 'Disallow throwing non-`Error` values as exceptions', - recommended: 'recommended', extendsBaseRule: 'no-throw-literal', + recommended: 'recommended', requiresTypeChecking: true, }, + messages: { + object: 'Expected an error object to be thrown.', + undef: 'Do not throw undefined.', + }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowThrowingAny: { + type: 'boolean', description: 'Whether to always allow throwing values typed as `any`.', - type: 'boolean', }, allowThrowingUnknown: { + type: 'boolean', description: 'Whether to always allow throwing values typed as `unknown`.', - type: 'boolean', }, }, - additionalProperties: false, }, ], - messages: { - object: 'Expected an error object to be thrown.', - undef: 'Do not throw undefined.', - }, }, defaultOptions: [ { diff --git a/packages/eslint-plugin/src/rules/parameter-properties.ts b/packages/eslint-plugin/src/rules/parameter-properties.ts index 34cd6da38140..227f30abc014 100644 --- a/packages/eslint-plugin/src/rules/parameter-properties.ts +++ b/packages/eslint-plugin/src/rules/parameter-properties.ts @@ -1,15 +1,16 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, nullThrows } from '../util'; type Modifier = - | 'private readonly' | 'private' - | 'protected readonly' + | 'private readonly' | 'protected' - | 'public readonly' + | 'protected readonly' | 'public' + | 'public readonly' | 'readonly'; type Prefer = 'class-property' | 'parameter-property'; @@ -39,6 +40,7 @@ export default createRule({ }, schema: [ { + type: 'object', $defs: { modifier: { type: 'string', @@ -53,24 +55,23 @@ export default createRule({ ], }, }, - type: 'object', + additionalProperties: false, properties: { allow: { + type: 'array', description: 'Whether to allow certain kinds of properties to be ignored.', - type: 'array', items: { $ref: '#/items/0/$defs/modifier', }, }, prefer: { + type: 'string', description: 'Whether to prefer class properties or parameter properties.', - type: 'string', enum: ['class-property', 'parameter-property'], }, }, - additionalProperties: false, }, ], }, @@ -173,10 +174,6 @@ export default createRule({ } return { - 'ClassDeclaration, ClassExpression'(): void { - propertyNodesByNameStack.push(new Map()); - }, - ':matches(ClassDeclaration, ClassExpression):exit'(): void { const propertyNodesByName = nullThrows( propertyNodesByNameStack.pop(), @@ -194,11 +191,11 @@ export default createRule({ ) ) { context.report({ + node: nodes.classProperty, + messageId: 'preferParameterProperty', data: { parameter: name, }, - messageId: 'preferParameterProperty', - node: nodes.classProperty, }); } } @@ -217,6 +214,10 @@ export default createRule({ } }, + 'ClassDeclaration, ClassExpression'(): void { + propertyNodesByNameStack.push(new Map()); + }, + 'MethodDefinition[kind="constructor"]'( node: TSESTree.MethodDefinition, ): void { diff --git a/packages/eslint-plugin/src/rules/prefer-as-const.ts b/packages/eslint-plugin/src/rules/prefer-as-const.ts index f3bcaf2963af..eb0a1e12008e 100644 --- a/packages/eslint-plugin/src/rules/prefer-as-const.ts +++ b/packages/eslint-plugin/src/rules/prefer-as-const.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -60,17 +61,17 @@ export default createRule({ } return { + PropertyDefinition(node): void { + if (node.value && node.typeAnnotation) { + compareTypes(node.value, node.typeAnnotation.typeAnnotation, false); + } + }, TSAsExpression(node): void { compareTypes(node.expression, node.typeAnnotation, true); }, TSTypeAssertion(node): void { compareTypes(node.expression, node.typeAnnotation, true); }, - PropertyDefinition(node): void { - if (node.value && node.typeAnnotation) { - compareTypes(node.value, node.typeAnnotation.typeAnnotation, false); - } - }, VariableDeclarator(node): void { if (node.init && node.id.typeAnnotation) { compareTypes(node.init, node.id.typeAnnotation.typeAnnotation, false); diff --git a/packages/eslint-plugin/src/rules/prefer-destructuring.ts b/packages/eslint-plugin/src/rules/prefer-destructuring.ts index 60e53dbb61e6..fdf3bc1deeaf 100644 --- a/packages/eslint-plugin/src/rules/prefer-destructuring.ts +++ b/packages/eslint-plugin/src/rules/prefer-destructuring.ts @@ -1,28 +1,31 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; -import * as tsutils from 'ts-api-utils'; import type * as ts from 'typescript'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import * as tsutils from 'ts-api-utils'; + import type { InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, } from '../util'; + import { createRule, getParserServices, isTypeAnyType } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; const baseRule = getESLintCoreRule('prefer-destructuring'); type BaseOptions = InferOptionsTypeFromRule; -type EnforcementOptions = BaseOptions[1] & { +type EnforcementOptions = { enforceForDeclarationWithTypeAnnotation?: boolean; -}; +} & BaseOptions[1]; type Options = [BaseOptions[0], EnforcementOptions]; type MessageIds = InferMessageIdsTypeFromRule; const destructuringTypeConfig: JSONSchema4 = { type: 'object', + additionalProperties: false, properties: { array: { type: 'boolean', @@ -31,7 +34,6 @@ const destructuringTypeConfig: JSONSchema4 = { type: 'boolean', }, }, - additionalProperties: false, }; const schema: readonly JSONSchema4[] = [ @@ -39,11 +41,11 @@ const schema: readonly JSONSchema4[] = [ oneOf: [ { type: 'object', + additionalProperties: false, properties: { - VariableDeclarator: destructuringTypeConfig, AssignmentExpression: destructuringTypeConfig, + VariableDeclarator: destructuringTypeConfig, }, - additionalProperties: false, }, destructuringTypeConfig, ], @@ -51,10 +53,10 @@ const schema: readonly JSONSchema4[] = [ { type: 'object', properties: { - enforceForRenamedProperties: { + enforceForDeclarationWithTypeAnnotation: { type: 'boolean', }, - enforceForDeclarationWithTypeAnnotation: { + enforceForRenamedProperties: { type: 'boolean', }, }, @@ -70,18 +72,18 @@ export default createRule({ extendsBaseRule: true, requiresTypeChecking: true, }, - schema, fixable: baseRule.meta.fixable, hasSuggestions: baseRule.meta.hasSuggestions, messages: baseRule.meta.messages, + schema, }, defaultOptions: [ { - VariableDeclarator: { + AssignmentExpression: { array: true, object: true, }, - AssignmentExpression: { + VariableDeclarator: { array: true, object: true, }, @@ -90,30 +92,30 @@ export default createRule({ ], create(context, [enabledTypes, options]) { const { - enforceForRenamedProperties = false, enforceForDeclarationWithTypeAnnotation = false, + enforceForRenamedProperties = false, } = options; - const { program, esTreeNodeToTSNodeMap } = getParserServices(context); + const { esTreeNodeToTSNodeMap, program } = getParserServices(context); const typeChecker = program.getTypeChecker(); const baseRules = baseRule.create(context); let baseRulesWithoutFixCache: typeof baseRules | null = null; return { - VariableDeclarator(node): void { - performCheck(node.id, node.init, node); - }, AssignmentExpression(node): void { if (node.operator !== '=') { return; } performCheck(node.left, node.right, node); }, + VariableDeclarator(node): void { + performCheck(node.id, node.init, node); + }, }; function performCheck( leftNode: TSESTree.BindingName | TSESTree.Expression, rightNode: TSESTree.Expression | null, - reportNode: TSESTree.VariableDeclarator | TSESTree.AssignmentExpression, + reportNode: TSESTree.AssignmentExpression | TSESTree.VariableDeclarator, ): void { const rules = leftNode.type === AST_NODE_TYPES.Identifier && @@ -162,8 +164,8 @@ export default createRule({ function getNormalizedEnabledType( nodeType: - | AST_NODE_TYPES.VariableDeclarator - | AST_NODE_TYPES.AssignmentExpression, + | AST_NODE_TYPES.AssignmentExpression + | AST_NODE_TYPES.VariableDeclarator, destructuringType: 'array' | 'object', ): boolean | undefined { if ('object' in enabledTypes || 'array' in enabledTypes) { diff --git a/packages/eslint-plugin/src/rules/prefer-find.ts b/packages/eslint-plugin/src/rules/prefer-find.ts index 389d4f581378..0a7c4d099542 100644 --- a/packages/eslint-plugin/src/rules/prefer-find.ts +++ b/packages/eslint-plugin/src/rules/prefer-find.ts @@ -1,9 +1,10 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { RuleFix } from '@typescript-eslint/utils/ts-eslint'; -import * as tsutils from 'ts-api-utils'; import type { Type } from 'typescript'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import * as tsutils from 'ts-api-utils'; + import { createRule, getConstrainedTypeAtLocation, @@ -16,19 +17,19 @@ import { export default createRule({ name: 'prefer-find', meta: { + type: 'suggestion', docs: { description: 'Enforce the use of Array.prototype.find() over Array.prototype.filter() followed by [0] when looking for a single result', recommended: 'stylistic', requiresTypeChecking: true, }, + hasSuggestions: true, messages: { preferFind: 'Prefer .find(...) instead of .filter(...)[0].', preferFindSuggestion: 'Use .find(...) instead of .filter(...)[0].', }, schema: [], - type: 'suggestion', - hasSuggestions: true, }, defaultOptions: [], @@ -39,8 +40,8 @@ export default createRule({ const checker = services.program.getTypeChecker(); interface FilterExpressionData { - isBracketSyntaxForFilter: boolean; filterNode: TSESTree.Node; + isBracketSyntaxForFilter: boolean; } function parseArrayFilterExpressions( @@ -103,8 +104,8 @@ export default createRule({ if (isArrayish(filteredObjectType)) { return [ { - isBracketSyntaxForFilter, filterNode, + isBracketSyntaxForFilter, }, ]; } diff --git a/packages/eslint-plugin/src/rules/prefer-for-of.ts b/packages/eslint-plugin/src/rules/prefer-for-of.ts index 18622a04e2ec..ce7ef8a8e180 100644 --- a/packages/eslint-plugin/src/rules/prefer-for-of.ts +++ b/packages/eslint-plugin/src/rules/prefer-for-of.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, isAssignee } from '../util'; diff --git a/packages/eslint-plugin/src/rules/prefer-function-type.ts b/packages/eslint-plugin/src/rules/prefer-function-type.ts index 74ee9ac968b7..4f4435a9ea94 100644 --- a/packages/eslint-plugin/src/rules/prefer-function-type.ts +++ b/packages/eslint-plugin/src/rules/prefer-function-type.ts @@ -1,16 +1,18 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; export const phrases = { - [AST_NODE_TYPES.TSTypeLiteral]: 'Type literal', [AST_NODE_TYPES.TSInterfaceDeclaration]: 'Interface', + [AST_NODE_TYPES.TSTypeLiteral]: 'Type literal', } as const; export default createRule({ name: 'prefer-function-type', meta: { + type: 'suggestion', docs: { description: 'Enforce using function types instead of interfaces with call signatures', @@ -24,7 +26,6 @@ export default createRule({ "`this` refers to the function type '{{ interfaceName }}', did you intend to use a generic `this` parameter like `(this: Self, ...) => Self` instead?", }, schema: [], - type: 'suggestion', }, defaultOptions: [], create(context) { @@ -193,14 +194,6 @@ export default createRule({ // when entering an interface reset the count of `this`s to empty. tsThisTypes = []; }, - 'TSInterfaceDeclaration TSThisType'(node: TSESTree.TSThisType): void { - // inside an interface keep track of all ThisType references. - // unless it's inside a nested type literal in which case it's invalid code anyway - // we don't want to incorrectly say "it refers to name" while typescript says it's completely invalid. - if (literalNesting === 0 && tsThisTypes != null) { - tsThisTypes.push(node); - } - }, 'TSInterfaceDeclaration:exit'( node: TSESTree.TSInterfaceDeclaration, ): void { @@ -210,6 +203,14 @@ export default createRule({ // on exit check member and reset the array to nothing. tsThisTypes = null; }, + 'TSInterfaceDeclaration TSThisType'(node: TSESTree.TSThisType): void { + // inside an interface keep track of all ThisType references. + // unless it's inside a nested type literal in which case it's invalid code anyway + // we don't want to incorrectly say "it refers to name" while typescript says it's completely invalid. + if (literalNesting === 0 && tsThisTypes != null) { + tsThisTypes.push(node); + } + }, // keep track of nested literals to avoid complaining about invalid `this` uses 'TSInterfaceDeclaration TSTypeLiteral'(): void { literalNesting += 1; diff --git a/packages/eslint-plugin/src/rules/prefer-includes.ts b/packages/eslint-plugin/src/rules/prefer-includes.ts index 98a24fcc8917..6825fc042a6d 100644 --- a/packages/eslint-plugin/src/rules/prefer-includes.ts +++ b/packages/eslint-plugin/src/rules/prefer-includes.ts @@ -1,5 +1,6 @@ -import { parseRegExpLiteral } from '@eslint-community/regexpp'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + +import { parseRegExpLiteral } from '@eslint-community/regexpp'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -13,8 +14,6 @@ import { export default createRule({ name: 'prefer-includes', - defaultOptions: [], - meta: { type: 'suggestion', docs: { @@ -31,6 +30,8 @@ export default createRule({ schema: [], }, + defaultOptions: [], + create(context) { const globalScope = context.sourceCode.getScope(context.sourceCode.ast); const services = getParserServices(context); @@ -103,7 +104,7 @@ export default createRule({ return null; } - const { pattern, flags } = parseRegExpLiteral(evaluated.value); + const { flags, pattern } = parseRegExpLiteral(evaluated.value); if ( pattern.alternatives.length !== 1 || flags.ignoreCase || @@ -125,13 +126,13 @@ export default createRule({ function escapeString(str: string): string { const EscapeMap = { '\0': '\\0', - "'": "\\'", - '\\': '\\\\', + '\t': '\\t', '\n': '\\n', - '\r': '\\r', '\v': '\\v', - '\t': '\\t', '\f': '\\f', + '\r': '\\r', + "'": "\\'", + '\\': '\\\\', // "\b" cause unexpected replacements // '\b': '\\b', }; @@ -223,7 +224,7 @@ export default createRule({ // /bar/.test(foo) 'CallExpression[arguments.length=1] > MemberExpression.callee[property.name="test"][computed=false]'( - node: TSESTree.MemberExpression & { parent: TSESTree.CallExpression }, + node: { parent: TSESTree.CallExpression } & TSESTree.MemberExpression, ): void { const callNode = node.parent; const text = parseRegExp(node.object); diff --git a/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts b/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts index 1535cca21ea7..e41f0bda09ef 100644 --- a/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts +++ b/packages/eslint-plugin/src/rules/prefer-literal-enum-member.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, getStaticStringValue } from '../util'; @@ -14,18 +15,19 @@ export default createRule({ }, messages: { notLiteral: `Explicit enum value must only be a literal value (string or number).`, + notLiteralOrBitwiseExpression: `Explicit enum value must only be a literal value (string or number) or a bitwise expression.`, }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowBitwiseExpressions: { + type: 'boolean', description: 'Whether to allow using bitwise expressions in enum initializers.', - type: 'boolean', }, }, - additionalProperties: false, }, ], }, @@ -106,7 +108,7 @@ export default createRule({ case AST_NODE_TYPES.UnaryExpression: // +123, -123, etc. - if (['+', '-'].includes(node.operator)) { + if (['-', '+'].includes(node.operator)) { return isAllowedInitializerExpressionRecursive( node.argument, partOfBitwiseComputation, @@ -124,7 +126,7 @@ export default createRule({ case AST_NODE_TYPES.BinaryExpression: if (allowBitwiseExpressions) { return ( - ['|', '&', '^', '<<', '>>', '>>>'].includes(node.operator) && + ['&', '^', '<<', '>>', '>>>', '|'].includes(node.operator) && isAllowedInitializerExpressionRecursive(node.left, true) && isAllowedInitializerExpressionRecursive(node.right, true) ); @@ -142,7 +144,9 @@ export default createRule({ context.report({ node: node.id, - messageId: 'notLiteral', + messageId: allowBitwiseExpressions + ? 'notLiteralOrBitwiseExpression' + : 'notLiteral', }); }, }; diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index 05de88296a4f..94424989b955 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -52,32 +53,33 @@ export default createRule({ }, hasSuggestions: true, messages: { + noStrictNullCheck: + 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', preferNullishOverOr: 'Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator.', preferNullishOverTernary: 'Prefer using nullish coalescing operator (`??`) instead of a ternary expression, as it is simpler to read.', suggestNullish: 'Fix to nullish coalescing operator (`??`).', - noStrictNullCheck: - 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: { + type: 'boolean', description: 'Unless this is set to `true`, the rule will error on every file whose `tsconfig.json` does _not_ have the `strictNullChecks` compiler option (or `strict`) set to `true`.', - type: 'boolean', }, ignoreConditionalTests: { + type: 'boolean', description: 'Whether to ignore cases that are located within a conditional test.', - type: 'boolean', }, ignoreMixedLogicalExpressions: { + type: 'boolean', description: 'Whether to ignore any logical or expressions that are part of a mixed logical expression (with `&&`).', - type: 'boolean', }, ignorePrimitives: { description: @@ -99,12 +101,11 @@ export default createRule({ ], }, ignoreTernaryTests: { + type: 'boolean', description: 'Whether to ignore any ternary expressions that could be simplified by using the nullish coalescing operator.', - type: 'boolean', }, }, - additionalProperties: false, }, ], }, @@ -112,7 +113,6 @@ export default createRule({ { allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, ignoreConditionalTests: true, - ignoreTernaryTests: false, ignoreMixedLogicalExpressions: false, ignorePrimitives: { bigint: false, @@ -120,6 +120,7 @@ export default createRule({ number: false, string: false, }, + ignoreTernaryTests: false, }, ], create( @@ -149,8 +150,8 @@ export default createRule({ ) { context.report({ loc: { - start: { line: 0, column: 0 }, - end: { line: 0, column: 0 }, + start: { column: 0, line: 0 }, + end: { column: 0, line: 0 }, }, messageId: 'noStrictNullCheck', }); diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts index b755c9463954..de1147b54447 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts @@ -1,14 +1,14 @@ export type PreferOptionalChainMessageIds = - | 'preferOptionalChain' - | 'optionalChainSuggest'; + | 'optionalChainSuggest' + | 'preferOptionalChain'; export interface PreferOptionalChainOptions { + allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing?: boolean; checkAny?: boolean; - checkUnknown?: boolean; - checkString?: boolean; - checkNumber?: boolean; - checkBoolean?: boolean; checkBigInt?: boolean; + checkBoolean?: boolean; + checkNumber?: boolean; + checkString?: boolean; + checkUnknown?: boolean; requireNullish?: boolean; - allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing?: boolean; } diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts index 8a3358ad7172..cd4894cc384c 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts @@ -2,16 +2,23 @@ import type { ParserServicesWithTypeInformation, TSESTree, } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { ReportDescriptor, ReportFixFunction, RuleContext, SourceCode, } from '@typescript-eslint/utils/ts-eslint'; + +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { unionTypeParts } from 'ts-api-utils'; import * as ts from 'typescript'; +import type { ValidOperand } from './gatherLogicalOperands'; +import type { + PreferOptionalChainMessageIds, + PreferOptionalChainOptions, +} from './PreferOptionalChainOptions'; + import { getFixOrSuggest, getOperatorPrecedenceForNode, @@ -24,12 +31,7 @@ import { } from '../../util'; import { checkNullishAndReport } from './checkNullishAndReport'; import { compareNodes, NodeComparisonResult } from './compareNodes'; -import type { ValidOperand } from './gatherLogicalOperands'; import { NullishComparisonType } from './gatherLogicalOperands'; -import type { - PreferOptionalChainMessageIds, - PreferOptionalChainOptions, -} from './PreferOptionalChainOptions'; function includesType( parserServices: ParserServicesWithTypeInformation, @@ -55,7 +57,7 @@ type OperandAnalyzer = ( operand: ValidOperand, index: number, chain: readonly ValidOperand[], -) => readonly [ValidOperand] | readonly [ValidOperand, ValidOperand] | null; +) => readonly [ValidOperand, ValidOperand] | readonly [ValidOperand] | null; const analyzeAndChainOperand: OperandAnalyzer = ( parserServices, operand, @@ -405,17 +407,17 @@ function getReportDescriptor( fixer.replaceTextRange(reportRange, newCode); return { - messageId: 'preferOptionalChain', loc: { - start: sourceCode.getLocFromIndex(reportRange[0]), end: sourceCode.getLocFromIndex(reportRange[1]), + start: sourceCode.getLocFromIndex(reportRange[0]), }, + messageId: 'preferOptionalChain', ...getFixOrSuggest({ - useFix: !useSuggestionFixer, suggestion: { - messageId: 'optionalChainSuggest', fix, + messageId: 'optionalChainSuggest', }, + useFix: !useSuggestionFixer, }), }; @@ -541,7 +543,7 @@ export function analyzeChain( // Things like x !== null && x !== undefined have two nodes, but they are // one logical unit here, so we'll allow them to be grouped. - let subChain: (ValidOperand | readonly ValidOperand[])[] = []; + let subChain: (readonly ValidOperand[] | ValidOperand)[] = []; const maybeReportThenReset = ( newChainSeed?: readonly [ValidOperand, ...ValidOperand[]], ): void => { diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/checkNullishAndReport.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/checkNullishAndReport.ts index 404b3736040a..158376eb7755 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/checkNullishAndReport.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/checkNullishAndReport.ts @@ -1,4 +1,3 @@ -import { isTypeFlagSet } from '@typescript-eslint/type-utils'; import type { ParserServicesWithTypeInformation, TSESTree, @@ -7,6 +6,8 @@ import type { ReportDescriptor, RuleContext, } from '@typescript-eslint/utils/ts-eslint'; + +import { isTypeFlagSet } from '@typescript-eslint/type-utils'; import { unionTypeParts } from 'ts-api-utils'; import * as ts from 'typescript'; diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts index d9dce486ec91..8cbc5469f0d7 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/compareNodes.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { visitorKeys } from '@typescript-eslint/visitor-keys'; diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts index f0411b2f23b4..9adb8225b6c3 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts @@ -2,8 +2,9 @@ import type { ParserServicesWithTypeInformation, TSESTree, } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { SourceCode } from '@typescript-eslint/utils/ts-eslint'; + +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { isBigIntLiteralType, isBooleanLiteralType, @@ -13,9 +14,10 @@ import { } from 'ts-api-utils'; import * as ts from 'typescript'; -import { isReferenceToGlobalFunction, isTypeFlagSet } from '../../util'; import type { PreferOptionalChainOptions } from './PreferOptionalChainOptions'; +import { isReferenceToGlobalFunction, isTypeFlagSet } from '../../util'; + const enum ComparisonValueType { Null = 'Null', // eslint-disable-line @typescript-eslint/internal/prefer-ast-types-enum Undefined = 'Undefined', @@ -47,16 +49,16 @@ export const enum NullishComparisonType { Boolean = 'Boolean', // eslint-disable-line @typescript-eslint/internal/prefer-ast-types-enum } export interface ValidOperand { - type: OperandValidity.Valid; comparedName: TSESTree.Node; comparisonType: NullishComparisonType; isYoda: boolean; node: TSESTree.Expression; + type: OperandValidity.Valid; } export interface InvalidOperand { type: OperandValidity.Invalid; } -type Operand = ValidOperand | InvalidOperand; +type Operand = InvalidOperand | ValidOperand; const NULLISH_FLAGS = ts.TypeFlags.Null | ts.TypeFlags.Undefined; function isValidFalseBooleanCheckType( @@ -118,11 +120,11 @@ export function gatherLogicalOperands( sourceCode: Readonly, options: PreferOptionalChainOptions, ): { - operands: Operand[]; newlySeenLogicals: Set; + operands: Operand[]; } { const result: Operand[] = []; - const { operands, newlySeenLogicals } = flattenLogicalOperands(node); + const { newlySeenLogicals, operands } = flattenLogicalOperands(node); for (const operand of operands) { const areMoreOperands = operand !== operands.at(-1); @@ -164,13 +166,13 @@ export function gatherLogicalOperands( // typeof x.y === 'undefined' result.push({ - type: OperandValidity.Valid, comparedName: comparedExpression.argument, comparisonType: operand.operator.startsWith('!') ? NullishComparisonType.NotStrictEqualUndefined : NullishComparisonType.StrictEqualUndefined, isYoda, node: operand, + type: OperandValidity.Valid, }); continue; } @@ -189,13 +191,13 @@ export function gatherLogicalOperands( ) { // x == null, x == undefined result.push({ - type: OperandValidity.Valid, comparedName: comparedExpression, comparisonType: operand.operator.startsWith('!') ? NullishComparisonType.NotEqualNullOrUndefined : NullishComparisonType.EqualNullOrUndefined, isYoda, node: operand, + type: OperandValidity.Valid, }); continue; } @@ -209,25 +211,25 @@ export function gatherLogicalOperands( switch (comparedValue) { case ComparisonValueType.Null: result.push({ - type: OperandValidity.Valid, comparedName, comparisonType: operand.operator.startsWith('!') ? NullishComparisonType.NotStrictEqualNull : NullishComparisonType.StrictEqualNull, isYoda, node: operand, + type: OperandValidity.Valid, }); continue; case ComparisonValueType.Undefined: result.push({ - type: OperandValidity.Valid, comparedName, comparisonType: operand.operator.startsWith('!') ? NullishComparisonType.NotStrictEqualUndefined : NullishComparisonType.StrictEqualUndefined, isYoda, node: operand, + type: OperandValidity.Valid, }); continue; @@ -254,11 +256,11 @@ export function gatherLogicalOperands( ) ) { result.push({ - type: OperandValidity.Valid, comparedName: operand.argument, comparisonType: NullishComparisonType.NotBoolean, isYoda: false, node: operand, + type: OperandValidity.Valid, }); continue; } @@ -280,11 +282,11 @@ export function gatherLogicalOperands( ) ) { result.push({ - type: OperandValidity.Valid, comparedName: operand, comparisonType: NullishComparisonType.Boolean, isYoda: false, node: operand, + type: OperandValidity.Valid, }); } else { result.push({ type: OperandValidity.Invalid }); @@ -294,8 +296,8 @@ export function gatherLogicalOperands( } return { - operands: result, newlySeenLogicals, + operands: result, }; /* @@ -320,8 +322,8 @@ export function gatherLogicalOperands( like `foo || foo.bar && foo.bar.baz` - separate selector */ function flattenLogicalOperands(node: TSESTree.LogicalExpression): { - operands: TSESTree.Expression[]; newlySeenLogicals: Set; + operands: TSESTree.Expression[]; } { const operands: TSESTree.Expression[] = []; const newlySeenLogicals = new Set([node]); @@ -342,8 +344,8 @@ export function gatherLogicalOperands( } return { - operands, newlySeenLogicals, + operands, }; } diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts index cea09251e53f..ffbcd213fedd 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts @@ -1,7 +1,14 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { RuleFix } from '@typescript-eslint/utils/ts-eslint'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + +import type { ValidOperand } from './prefer-optional-chain-utils/gatherLogicalOperands'; +import type { + PreferOptionalChainMessageIds, + PreferOptionalChainOptions, +} from './prefer-optional-chain-utils/PreferOptionalChainOptions'; + import { createRule, getOperatorPrecedence, @@ -10,15 +17,10 @@ import { } from '../util'; import { analyzeChain } from './prefer-optional-chain-utils/analyzeChain'; import { checkNullishAndReport } from './prefer-optional-chain-utils/checkNullishAndReport'; -import type { ValidOperand } from './prefer-optional-chain-utils/gatherLogicalOperands'; import { gatherLogicalOperands, OperandValidity, } from './prefer-optional-chain-utils/gatherLogicalOperands'; -import type { - PreferOptionalChainMessageIds, - PreferOptionalChainOptions, -} from './prefer-optional-chain-utils/PreferOptionalChainOptions'; export default createRule< [PreferOptionalChainOptions], @@ -36,69 +38,69 @@ export default createRule< fixable: 'code', hasSuggestions: true, messages: { + optionalChainSuggest: 'Change to an optional chain.', preferOptionalChain: "Prefer using an optional chain expression instead, as it's more concise and easier to read.", - optionalChainSuggest: 'Change to an optional chain.', }, schema: [ { type: 'object', additionalProperties: false, properties: { + allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing: { + type: 'boolean', + description: + 'Allow autofixers that will change the return type of the expression. This option is considered unsafe as it may break the build.', + }, checkAny: { type: 'boolean', description: 'Check operands that are typed as `any` when inspecting "loose boolean" operands.', }, - checkUnknown: { + checkBigInt: { type: 'boolean', description: - 'Check operands that are typed as `unknown` when inspecting "loose boolean" operands.', + 'Check operands that are typed as `bigint` when inspecting "loose boolean" operands.', }, - checkString: { + checkBoolean: { type: 'boolean', description: - 'Check operands that are typed as `string` when inspecting "loose boolean" operands.', + 'Check operands that are typed as `boolean` when inspecting "loose boolean" operands.', }, checkNumber: { type: 'boolean', description: 'Check operands that are typed as `number` when inspecting "loose boolean" operands.', }, - checkBoolean: { + checkString: { type: 'boolean', description: - 'Check operands that are typed as `boolean` when inspecting "loose boolean" operands.', + 'Check operands that are typed as `string` when inspecting "loose boolean" operands.', }, - checkBigInt: { + checkUnknown: { type: 'boolean', description: - 'Check operands that are typed as `bigint` when inspecting "loose boolean" operands.', + 'Check operands that are typed as `unknown` when inspecting "loose boolean" operands.', }, requireNullish: { type: 'boolean', description: 'Skip operands that are not typed with `null` and/or `undefined` when inspecting "loose boolean" operands.', }, - allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing: { - type: 'boolean', - description: - 'Allow autofixers that will change the return type of the expression. This option is considered unsafe as it may break the build.', - }, }, }, ], }, defaultOptions: [ { + allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing: false, checkAny: true, - checkUnknown: true, - checkString: true, - checkNumber: true, - checkBoolean: true, checkBigInt: true, + checkBoolean: true, + checkNumber: true, + checkString: true, + checkUnknown: true, requireNullish: false, - allowPotentiallyUnsafeFixesThatModifyTheReturnTypeIKnowWhatImDoing: false, }, ], create(context, [options]) { @@ -108,6 +110,53 @@ export default createRule< return { // specific handling for `(foo ?? {}).bar` / `(foo || {}).bar` + 'LogicalExpression[operator!="??"]'( + node: TSESTree.LogicalExpression, + ): void { + if (seenLogicals.has(node)) { + return; + } + + const { newlySeenLogicals, operands } = gatherLogicalOperands( + node, + parserServices, + context.sourceCode, + options, + ); + for (const logical of newlySeenLogicals) { + seenLogicals.add(logical); + } + + let currentChain: ValidOperand[] = []; + for (const operand of operands) { + if (operand.type === OperandValidity.Invalid) { + analyzeChain( + context, + parserServices, + options, + node, + node.operator, + currentChain, + ); + currentChain = []; + } else { + currentChain.push(operand); + } + } + + // make sure to check whatever's left + if (currentChain.length > 0) { + analyzeChain( + context, + parserServices, + options, + node, + node.operator, + currentChain, + ); + } + }, + 'LogicalExpression[operator="||"], LogicalExpression[operator="??"]'( node: TSESTree.LogicalExpression, ): void { @@ -138,8 +187,8 @@ export default createRule< return leftPrecedence < OperatorPrecedence.LeftHandSide; } checkNullishAndReport(context, parserServices, options, [leftNode], { - messageId: 'preferOptionalChain', node: parentNode, + messageId: 'preferOptionalChain', suggest: [ { messageId: 'optionalChainSuggest', @@ -164,53 +213,6 @@ export default createRule< ], }); }, - - 'LogicalExpression[operator!="??"]'( - node: TSESTree.LogicalExpression, - ): void { - if (seenLogicals.has(node)) { - return; - } - - const { operands, newlySeenLogicals } = gatherLogicalOperands( - node, - parserServices, - context.sourceCode, - options, - ); - for (const logical of newlySeenLogicals) { - seenLogicals.add(logical); - } - - let currentChain: ValidOperand[] = []; - for (const operand of operands) { - if (operand.type === OperandValidity.Invalid) { - analyzeChain( - context, - parserServices, - options, - node, - node.operator, - currentChain, - ); - currentChain = []; - } else { - currentChain.push(operand); - } - } - - // make sure to check whatever's left - if (currentChain.length > 0) { - analyzeChain( - context, - parserServices, - options, - node, - node.operator, - currentChain, - ); - } - }, }; }, }); diff --git a/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts b/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts index acad1d6ef8f3..40b4d7f59cf7 100644 --- a/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts +++ b/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { @@ -27,26 +28,26 @@ export default createRule({ type: 'suggestion', docs: { description: 'Require using Error objects as Promise rejection reasons', - recommended: 'recommended', extendsBaseRule: true, + recommended: 'recommended', requiresTypeChecking: true, }, + messages: { + rejectAnError: 'Expected the Promise rejection reason to be an Error.', + }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowEmptyReject: { + type: 'boolean', description: 'Whether to allow calls to `Promise.reject()` with no arguments.', - type: 'boolean', }, }, - additionalProperties: false, }, ], - messages: { - rejectAnError: 'Expected the Promise rejection reason to be an Error.', - }, }, defaultOptions: [ { diff --git a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts index edc66bf2a76e..0c90c4a4f85a 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts @@ -1,7 +1,9 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { TypeOrValueSpecifier } from '../util'; + import { createRule, getParserServices, @@ -29,6 +31,9 @@ export default createRule({ 'Require function parameters to be typed as `readonly` to prevent accidental mutation of inputs', requiresTypeChecking: true, }, + messages: { + shouldBeReadonly: 'Parameter should be a read only type.', + }, schema: [ { type: 'object', @@ -39,13 +44,13 @@ export default createRule({ description: 'An array of type specifiers to ignore.', }, checkParameterProperties: { - description: 'Whether to check class parameter properties.', type: 'boolean', + description: 'Whether to check class parameter properties.', }, ignoreInferredTypes: { + type: 'boolean', description: "Whether to ignore parameters which don't explicitly specify a type.", - type: 'boolean', }, treatMethodsAsReadonly: { ...readonlynessOptionsSchema.properties.treatMethodsAsReadonly, @@ -55,9 +60,6 @@ export default createRule({ }, }, ], - messages: { - shouldBeReadonly: 'Parameter should be a read only type.', - }, }, defaultOptions: [ { @@ -123,8 +125,8 @@ export default createRule({ const type = services.getTypeAtLocation(actualParam); const isReadOnly = isTypeReadonly(services.program, type, { - treatMethodsAsReadonly: !!treatMethodsAsReadonly, allow, + treatMethodsAsReadonly: !!treatMethodsAsReadonly, }); if (!isReadOnly) { diff --git a/packages/eslint-plugin/src/rules/prefer-readonly.ts b/packages/eslint-plugin/src/rules/prefer-readonly.ts index 3dab8b7f82a2..53b906afaaf4 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -31,6 +32,7 @@ const functionScopeBoundaries = [ export default createRule({ name: 'prefer-readonly', meta: { + type: 'suggestion', docs: { description: "Require private members to be marked as `readonly` if they're never modified outside of the constructor", @@ -43,18 +45,17 @@ export default createRule({ }, schema: [ { + type: 'object', additionalProperties: false, properties: { onlyInlineLambdas: { + type: 'boolean', description: 'Whether to restrict checking only to members immediately assigned a lambda value.', - type: 'boolean', }, }, - type: 'object', }, ], - type: 'suggestion', }, defaultOptions: [{ onlyInlineLambdas: false }], create(context, [{ onlyInlineLambdas }]) { @@ -173,6 +174,19 @@ export default createRule({ } return { + [`${functionScopeBoundaries}:exit`]( + node: + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.MethodDefinition, + ): void { + if (ASTUtils.isConstructor(node)) { + classScopeStack[classScopeStack.length - 1].exitConstructor(); + } else if (isFunctionScopeBoundaryInStack(node)) { + classScopeStack[classScopeStack.length - 1].exitNonConstructor(); + } + }, 'ClassDeclaration, ClassExpression'( node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, ): void { @@ -195,8 +209,8 @@ export default createRule({ getEsNodesFromViolatingNode(violatingNode); const reportNodeOrLoc: - | { node: TSESTree.Node } - | { loc: TSESTree.SourceLocation } = (() => { + | { loc: TSESTree.SourceLocation } + | { node: TSESTree.Node } = (() => { switch (esNode.type) { case AST_NODE_TYPES.MethodDefinition: case AST_NODE_TYPES.PropertyDefinition: @@ -217,26 +231,14 @@ export default createRule({ context.report({ ...reportNodeOrLoc, + messageId: 'preferReadonly', data: { name: context.sourceCode.getText(nameNode), }, fix: fixer => fixer.insertTextBefore(nameNode, 'readonly '), - messageId: 'preferReadonly', }); } }, - MemberExpression(node): void { - if (classScopeStack.length !== 0 && !node.computed) { - const tsNode = services.esTreeNodeToTSNodeMap.get( - node, - ) as ts.PropertyAccessExpression; - handlePropertyAccessExpression( - tsNode, - tsNode.parent, - classScopeStack[classScopeStack.length - 1], - ); - } - }, [functionScopeBoundaries]( node: | TSESTree.ArrowFunctionExpression @@ -252,17 +254,16 @@ export default createRule({ classScopeStack[classScopeStack.length - 1].enterNonConstructor(); } }, - [`${functionScopeBoundaries}:exit`]( - node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.MethodDefinition, - ): void { - if (ASTUtils.isConstructor(node)) { - classScopeStack[classScopeStack.length - 1].exitConstructor(); - } else if (isFunctionScopeBoundaryInStack(node)) { - classScopeStack[classScopeStack.length - 1].exitNonConstructor(); + MemberExpression(node): void { + if (classScopeStack.length !== 0 && !node.computed) { + const tsNode = services.esTreeNodeToTSNodeMap.get( + node, + ) as ts.PropertyAccessExpression; + handlePropertyAccessExpression( + tsNode, + tsNode.parent, + classScopeStack[classScopeStack.length - 1], + ); } }, }; @@ -284,20 +285,20 @@ enum TypeToClassRelation { } class ClassScope { + private readonly classType: ts.Type; + private constructorScopeDepth = OUTSIDE_CONSTRUCTOR; + private readonly memberVariableModifications = new Set(); private readonly privateModifiableMembers = new Map< string, ParameterOrPropertyDeclaration >(); + private readonly privateModifiableStatics = new Map< string, ParameterOrPropertyDeclaration >(); - private readonly memberVariableModifications = new Set(); - private readonly staticVariableModifications = new Set(); - private readonly classType: ts.Type; - - private constructorScopeDepth = OUTSIDE_CONSTRUCTOR; + private readonly staticVariableModifications = new Set(); public constructor( private readonly checker: ts.TypeChecker, @@ -347,50 +348,6 @@ class ClassScope { ).set(node.name.getText(), node); } - public getTypeToClassRelation(type: ts.Type): TypeToClassRelation { - if (type.isIntersection()) { - let result: TypeToClassRelation = TypeToClassRelation.None; - for (const subType of type.types) { - const subTypeResult = this.getTypeToClassRelation(subType); - switch (subTypeResult) { - case TypeToClassRelation.Class: - if (result === TypeToClassRelation.Instance) { - return TypeToClassRelation.ClassAndInstance; - } - result = TypeToClassRelation.Class; - break; - case TypeToClassRelation.Instance: - if (result === TypeToClassRelation.Class) { - return TypeToClassRelation.ClassAndInstance; - } - result = TypeToClassRelation.Instance; - break; - } - } - return result; - } - if (type.isUnion()) { - // any union of class/instance and something else will prevent access to - // private members, so we assume that union consists only of classes - // or class instances, because otherwise tsc will report an error - return this.getTypeToClassRelation(type.types[0]); - } - - if (!type.getSymbol() || !typeIsOrHasBaseType(type, this.classType)) { - return TypeToClassRelation.None; - } - - const typeIsClass = - tsutils.isObjectType(type) && - tsutils.isObjectFlagSet(type, ts.ObjectFlags.Anonymous); - - if (typeIsClass) { - return TypeToClassRelation.Class; - } - - return TypeToClassRelation.Instance; - } - public addVariableModification(node: ts.PropertyAccessExpression): void { const modifierType = this.checker.getTypeAtLocation(node.expression); @@ -434,16 +391,16 @@ class ClassScope { } } - public exitConstructor(): void { - this.constructorScopeDepth = OUTSIDE_CONSTRUCTOR; - } - public enterNonConstructor(): void { if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) { this.constructorScopeDepth += 1; } } + public exitConstructor(): void { + this.constructorScopeDepth = OUTSIDE_CONSTRUCTOR; + } + public exitNonConstructor(): void { if (this.constructorScopeDepth !== OUTSIDE_CONSTRUCTOR) { this.constructorScopeDepth -= 1; @@ -464,4 +421,48 @@ class ClassScope { ...this.privateModifiableStatics.values(), ]; } + + public getTypeToClassRelation(type: ts.Type): TypeToClassRelation { + if (type.isIntersection()) { + let result: TypeToClassRelation = TypeToClassRelation.None; + for (const subType of type.types) { + const subTypeResult = this.getTypeToClassRelation(subType); + switch (subTypeResult) { + case TypeToClassRelation.Class: + if (result === TypeToClassRelation.Instance) { + return TypeToClassRelation.ClassAndInstance; + } + result = TypeToClassRelation.Class; + break; + case TypeToClassRelation.Instance: + if (result === TypeToClassRelation.Class) { + return TypeToClassRelation.ClassAndInstance; + } + result = TypeToClassRelation.Instance; + break; + } + } + return result; + } + if (type.isUnion()) { + // any union of class/instance and something else will prevent access to + // private members, so we assume that union consists only of classes + // or class instances, because otherwise tsc will report an error + return this.getTypeToClassRelation(type.types[0]); + } + + if (!type.getSymbol() || !typeIsOrHasBaseType(type, this.classType)) { + return TypeToClassRelation.None; + } + + const typeIsClass = + tsutils.isObjectType(type) && + tsutils.isObjectFlagSet(type, ts.ObjectFlags.Anonymous); + + if (typeIsClass) { + return TypeToClassRelation.Class; + } + + return TypeToClassRelation.Instance; + } } diff --git a/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts b/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts index a8c820a7b370..e3ff336f2b70 100644 --- a/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts +++ b/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts @@ -1,7 +1,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import * as tsutils from 'ts-api-utils'; import type * as ts from 'typescript'; +import * as tsutils from 'ts-api-utils'; + import { createRule, getConstrainedTypeAtLocation, @@ -10,9 +11,9 @@ import { isTypeAssertion, } from '../util'; -type MemberExpressionWithCallExpressionParent = TSESTree.MemberExpression & { +type MemberExpressionWithCallExpressionParent = { parent: TSESTree.CallExpression; -}; +} & TSESTree.MemberExpression; export default createRule({ name: 'prefer-reduce-type-parameter', @@ -24,11 +25,11 @@ export default createRule({ recommended: 'strict', requiresTypeChecking: true, }, + fixable: 'code', messages: { preferTypeParameter: 'Unnecessary cast: Array#reduce accepts a type parameter for the default value.', }, - fixable: 'code', schema: [], }, defaultOptions: [], @@ -69,8 +70,8 @@ export default createRule({ // Check the owner type of the `reduce` method. if (isArrayType(calleeObjType)) { context.report({ - messageId: 'preferTypeParameter', node: secondArg, + messageId: 'preferTypeParameter', fix: fixer => { const fixes = [ fixer.removeRange([ diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts index 9254589a624e..6799ce8ad59f 100644 --- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts +++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts @@ -1,7 +1,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; +import type * as ts from 'typescript'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; -import type * as ts from 'typescript'; import { createRule, @@ -21,23 +22,23 @@ enum ArgumentType { export default createRule({ name: 'prefer-regexp-exec', - defaultOptions: [], - meta: { type: 'suggestion', - fixable: 'code', docs: { description: 'Enforce `RegExp#exec` over `String#match` if no global flag is provided', recommended: 'stylistic', requiresTypeChecking: true, }, + fixable: 'code', messages: { regExpExecOverStringMatch: 'Use the `RegExp#exec()` method instead.', }, schema: [], }, + defaultOptions: [], + create(context) { const globalScope = context.sourceCode.getScope(context.sourceCode.ast); const services = getParserServices(context); @@ -139,9 +140,9 @@ export default createRule({ node: memberNode.property, messageId: 'regExpExecOverStringMatch', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: callNode, innerNode: [objectNode], + sourceCode: context.sourceCode, wrap: objectCode => `${regExp.toString()}.exec(${objectCode})`, }), }); @@ -157,9 +158,9 @@ export default createRule({ node: memberNode.property, messageId: 'regExpExecOverStringMatch', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: callNode, innerNode: [objectNode, argumentNode], + sourceCode: context.sourceCode, wrap: (objectCode, argumentCode) => `${argumentCode}.exec(${objectCode})`, }), @@ -170,9 +171,9 @@ export default createRule({ node: memberNode.property, messageId: 'regExpExecOverStringMatch', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: callNode, innerNode: [objectNode, argumentNode], + sourceCode: context.sourceCode, wrap: (objectCode, argumentCode) => `RegExp(${argumentCode}).exec(${objectCode})`, }), diff --git a/packages/eslint-plugin/src/rules/prefer-return-this-type.ts b/packages/eslint-plugin/src/rules/prefer-return-this-type.ts index 80e55427078d..304f63dd1027 100644 --- a/packages/eslint-plugin/src/rules/prefer-return-this-type.ts +++ b/packages/eslint-plugin/src/rules/prefer-return-this-type.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -14,8 +15,6 @@ type FunctionLike = export default createRule({ name: 'prefer-return-this-type', - defaultOptions: [], - meta: { type: 'suggestion', docs: { @@ -24,13 +23,15 @@ export default createRule({ recommended: 'strict', requiresTypeChecking: true, }, + fixable: 'code', messages: { useThisType: 'Use `this` type instead.', }, schema: [], - fixable: 'code', }, + defaultOptions: [], + create(context) { const services = getParserServices(context); const checker = services.program.getTypeChecker(); diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts index ca0bd74c03bd..259272d3dd37 100644 --- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts +++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts @@ -1,5 +1,6 @@ -import { RegExpParser } from '@eslint-community/regexpp'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + +import { RegExpParser } from '@eslint-community/regexpp'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { @@ -29,8 +30,6 @@ type MessageIds = 'preferEndsWith' | 'preferStartsWith'; export default createRule({ name: 'prefer-string-starts-ends-with', - defaultOptions: [{ allowSingleElementEquality: 'never' }], - meta: { type: 'suggestion', docs: { @@ -39,27 +38,29 @@ export default createRule({ recommended: 'stylistic', requiresTypeChecking: true, }, + fixable: 'code', messages: { - preferStartsWith: "Use 'String#startsWith' method instead.", preferEndsWith: "Use the 'String#endsWith' method instead.", + preferStartsWith: "Use 'String#startsWith' method instead.", }, schema: [ { + type: 'object', additionalProperties: false, properties: { allowSingleElementEquality: { + type: 'string', description: 'Whether to allow equality checks against the first or last element of a string.', enum: ['always', 'never'], - type: 'string', }, }, - type: 'object', }, ], - fixable: 'code', }, + defaultOptions: [{ allowSingleElementEquality: 'never' }], + create(context, [{ allowSingleElementEquality }]) { const globalScope = context.sourceCode.getScope(context.sourceCode.ast); @@ -279,13 +280,13 @@ export default createRule({ */ function parseRegExp( node: TSESTree.Node, - ): { isStartsWith: boolean; isEndsWith: boolean; text: string } | null { + ): { isEndsWith: boolean; isStartsWith: boolean; text: string } | null { const evaluated = getStaticValue(node, globalScope); if (evaluated == null || !(evaluated.value instanceof RegExp)) { return null; } - const { source, flags } = evaluated.value; + const { flags, source } = evaluated.value; const isStartsWith = source.startsWith('^'); const isEndsWith = source.endsWith('$'); if ( diff --git a/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts index 6ae1f11720e1..dc01b4a59a08 100644 --- a/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts +++ b/packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts @@ -1,7 +1,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import type { RuleFix, RuleFixer } from '@typescript-eslint/utils/ts-eslint'; +import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; + import { createRule } from '../util'; type MessageIds = 'preferExpectErrorComment'; @@ -11,7 +12,6 @@ export default createRule<[], MessageIds>({ meta: { type: 'problem', deprecated: true, - replacedBy: ['@typescript-eslint/ban-ts-comment'], docs: { description: 'Enforce using `@ts-expect-error` over `@ts-ignore`', }, @@ -20,6 +20,7 @@ export default createRule<[], MessageIds>({ preferExpectErrorComment: 'Use "@ts-expect-error" to ensure an error is actually being suppressed.', }, + replacedBy: ['@typescript-eslint/ban-ts-comment'], schema: [], }, defaultOptions: [], diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index 942ef6b8fa46..d4d63fd7d3a4 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -28,51 +29,51 @@ export default createRule({ name: 'promise-function-async', meta: { type: 'suggestion', - fixable: 'code', docs: { description: 'Require any function or method that returns a Promise to be marked async', requiresTypeChecking: true, }, + fixable: 'code', messages: { missingAsync: 'Functions that return promises must be async.', }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowAny: { + type: 'boolean', description: 'Whether to consider `any` and `unknown` to be Promises.', - type: 'boolean', }, allowedPromiseNames: { + type: 'array', description: 'Any extra names of classes or interfaces to be considered Promises.', - type: 'array', items: { type: 'string', }, }, checkArrowFunctions: { - description: 'Whether to check arrow functions.', type: 'boolean', + description: 'Whether to check arrow functions.', }, checkFunctionDeclarations: { - description: 'Whether to check standalone function declarations.', type: 'boolean', + description: 'Whether to check standalone function declarations.', }, checkFunctionExpressions: { - description: 'Whether to check inline function expressions', type: 'boolean', + description: 'Whether to check inline function expressions', }, checkMethodDeclarations: { + type: 'boolean', description: 'Whether to check methods on classes and object literals.', - type: 'boolean', }, }, - additionalProperties: false, }, ], }, @@ -151,16 +152,16 @@ export default createRule({ if (isTypeFlagSet(returnType, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { // Report without auto fixer because the return type is unknown return context.report({ - messageId: 'missingAsync', - node, loc: getFunctionHeadLoc(node, context.sourceCode), + node, + messageId: 'missingAsync', }); } context.report({ - messageId: 'missingAsync', - node, loc: getFunctionHeadLoc(node, context.sourceCode), + node, + messageId: 'missingAsync', fix: fixer => { if ( node.parent.type === AST_NODE_TYPES.MethodDefinition || diff --git a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts index ec1f1e1fdb15..2f19e56f4d63 100644 --- a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts +++ b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts @@ -18,12 +18,6 @@ export type MessageIds = 'requireCompare'; export default createRule({ name: 'require-array-sort-compare', - defaultOptions: [ - { - ignoreStringArrays: true, - }, - ], - meta: { type: 'problem', docs: { @@ -40,15 +34,21 @@ export default createRule({ additionalProperties: false, properties: { ignoreStringArrays: { + type: 'boolean', description: 'Whether to ignore arrays in which all elements are strings.', - type: 'boolean', }, }, }, ], }, + defaultOptions: [ + { + ignoreStringArrays: true, + }, + ], + create(context, [options]) { const services = getParserServices(context); const checker = services.program.getTypeChecker(); diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index e08b4a895a43..9d0fd1d40ffa 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -1,9 +1,10 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import type { AST, RuleFix } from '@typescript-eslint/utils/ts-eslint'; -import * as tsutils from 'ts-api-utils'; import type * as ts from 'typescript'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; +import * as tsutils from 'ts-api-utils'; + import { createRule, getFunctionHeadLoc, @@ -16,11 +17,11 @@ import { } from '../util'; interface ScopeInfo { - upper: ScopeInfo | null; - hasAwait: boolean; hasAsync: boolean; - isGen: boolean; + hasAwait: boolean; isAsyncYield: boolean; + isGen: boolean; + upper: ScopeInfo | null; } type FunctionNode = | TSESTree.ArrowFunctionExpression @@ -34,16 +35,16 @@ export default createRule({ docs: { description: 'Disallow async functions which do not return promises and have no `await` expression', + extendsBaseRule: true, recommended: 'recommended', requiresTypeChecking: true, - extendsBaseRule: true, }, - schema: [], + hasSuggestions: true, messages: { missingAwait: "{{name}} has no 'await' expression.", removeAsync: "Remove 'async'.", }, - hasSuggestions: true, + schema: [], }, defaultOptions: [], create(context) { @@ -57,11 +58,11 @@ export default createRule({ */ function enterFunction(node: FunctionNode): void { scopeInfo = { - upper: scopeInfo, - hasAwait: false, hasAsync: node.async, - isGen: node.generator || false, + hasAwait: false, isAsyncYield: false, + isGen: node.generator || false, + upper: scopeInfo, }; } @@ -186,8 +187,8 @@ export default createRule({ } context.report({ - node, loc: getFunctionHeadLoc(node, context.sourceCode), + node, messageId: 'missingAwait', data: { name: upperCaseFirst(getFunctionNameWithKind(node)), @@ -268,16 +269,16 @@ export default createRule({ } return { - FunctionDeclaration: enterFunction, - FunctionExpression: enterFunction, ArrowFunctionExpression: enterFunction, - 'FunctionDeclaration:exit': exitFunction, - 'FunctionExpression:exit': exitFunction, 'ArrowFunctionExpression:exit': exitFunction, - AwaitExpression: markAsHasAwait, - 'VariableDeclaration[kind = "await using"]': markAsHasAwait, 'ForOfStatement[await = true]': markAsHasAwait, + FunctionDeclaration: enterFunction, + 'FunctionDeclaration:exit': exitFunction, + + FunctionExpression: enterFunction, + 'FunctionExpression:exit': exitFunction, + 'VariableDeclaration[kind = "await using"]': markAsHasAwait, YieldExpression: visitYieldExpression, // check body-less async arrow function. diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index 1953c15c70cb..306342754018 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -59,30 +60,30 @@ export default createRule({ additionalProperties: false, properties: { allowAny: { - description: 'Whether to allow `any` typed values.', type: 'boolean', + description: 'Whether to allow `any` typed values.', }, allowBoolean: { - description: 'Whether to allow `boolean` typed values.', type: 'boolean', + description: 'Whether to allow `boolean` typed values.', }, allowNullish: { + type: 'boolean', description: 'Whether to allow potentially `null` or `undefined` typed values.', - type: 'boolean', }, allowNumberAndString: { + type: 'boolean', description: 'Whether to allow `bigint`/`number` typed values and `string` typed values to be added together.', - type: 'boolean', }, allowRegExp: { - description: 'Whether to allow `regexp` typed values.', type: 'boolean', + description: 'Whether to allow `regexp` typed values.', }, skipCompoundAssignments: { - description: 'Whether to skip compound assignments such as `+=`.', type: 'boolean', + description: 'Whether to skip compound assignments such as `+=`.', }, }, }, @@ -171,12 +172,12 @@ export default createRule({ isTypeFlagSet(baseType, ts.TypeFlags.Null | ts.TypeFlags.Undefined)) ) { context.report({ + node: baseNode, + messageId: 'invalid', data: { - stringLike, type: typeChecker.typeToString(baseType), + stringLike, }, - messageId: 'invalid', - node: baseNode, }); hadIndividualComplaint = true; continue; @@ -193,12 +194,12 @@ export default createRule({ isDeeplyObjectType(subBaseType) ) { context.report({ + node: baseNode, + messageId: 'invalid', data: { - stringLike, type: typeChecker.typeToString(subBaseType), + stringLike, }, - messageId: 'invalid', - node: baseNode, }); hadIndividualComplaint = true; continue; @@ -220,13 +221,13 @@ export default createRule({ isTypeFlagSetInUnion(otherType, ts.TypeFlags.NumberLike) ) { return context.report({ + node, + messageId: 'mismatched', data: { - stringLike, left: typeChecker.typeToString(leftType), right: typeChecker.typeToString(rightType), + stringLike, }, - messageId: 'mismatched', - node, }); } @@ -235,12 +236,12 @@ export default createRule({ isTypeFlagSetInUnion(otherType, ts.TypeFlags.BigIntLike) ) { return context.report({ + node, + messageId: 'bigintAndNumber', data: { left: typeChecker.typeToString(leftType), right: typeChecker.typeToString(rightType), }, - messageId: 'bigintAndNumber', - node, }); } } diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 54b94aa0b193..98933d502726 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -1,13 +1,15 @@ +import type { TSESTree } from '@typescript-eslint/utils'; +import type { Type, TypeChecker } from 'typescript'; + import { typeMatchesSomeSpecifier, typeOrValueSpecifiersSchema, } from '@typescript-eslint/type-utils'; -import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import type { Type, TypeChecker } from 'typescript'; import { TypeFlags } from 'typescript'; import type { TypeOrValueSpecifier } from '../util'; + import { createRule, getConstrainedTypeAtLocation, @@ -75,10 +77,10 @@ export default createRule({ { allowAny: false, allowBoolean: false, + allowNever: false, allowNullish: false, allowNumber: false, allowRegExp: false, - allowNever: false, }, ], }, @@ -93,11 +95,11 @@ export default createRule({ additionalProperties: false, properties: { ...Object.fromEntries( - optionTesters.map(({ option, type }) => [ + optionTesters.map(({ type, option }) => [ option, { - description: `Whether to allow \`${type.toLowerCase()}\` typed values in template expressions.`, type: 'boolean', + description: `Whether to allow \`${type.toLowerCase()}\` typed values in template expressions.`, }, ]), ), @@ -111,12 +113,12 @@ export default createRule({ }, defaultOptions: [ { + allow: [{ name: ['Error', 'URL', 'URLSearchParams'], from: 'lib' }], allowAny: true, allowBoolean: true, allowNullish: true, allowNumber: true, allowRegExp: true, - allow: [{ from: 'lib', name: ['Error', 'URL', 'URLSearchParams'] }], }, ], create(context, [{ allow, ...options }]) { diff --git a/packages/eslint-plugin/src/rules/return-await.ts b/packages/eslint-plugin/src/rules/return-await.ts index 5152cc61466f..08ad959e6c26 100644 --- a/packages/eslint-plugin/src/rules/return-await.ts +++ b/packages/eslint-plugin/src/rules/return-await.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -25,36 +26,36 @@ interface ScopeInfo { } type Option = - | 'in-try-catch' | 'always' - | 'never' - | 'error-handling-correctness-only'; + | 'error-handling-correctness-only' + | 'in-try-catch' + | 'never'; export default createRule({ name: 'return-await', meta: { + type: 'problem', docs: { description: 'Enforce consistent awaiting of returned promises', - requiresTypeChecking: true, extendsBaseRule: 'no-return-await', recommended: { strict: ['error-handling-correctness-only'], }, + requiresTypeChecking: true, }, fixable: 'code', hasSuggestions: true, - type: 'problem', messages: { - nonPromiseAwait: - 'Returning an awaited value that is not a promise is not allowed.', disallowedPromiseAwait: 'Returning an awaited promise is not allowed in this context.', + disallowedPromiseAwaitSuggestion: + 'Remove `await` before the expression. Use caution as this may impact control flow.', + nonPromiseAwait: + 'Returning an awaited value that is not a promise is not allowed.', requiredPromiseAwait: 'Returning an awaited promise is required in this context.', requiredPromiseAwaitSuggestion: 'Add `await` before the expression. Use caution as this may impact control flow.', - disallowedPromiseAwaitSuggestion: - 'Remove `await` before the expression. Use caution as this may impact control flow.', }, schema: [ { @@ -62,25 +63,25 @@ export default createRule({ oneOf: [ { type: 'string', - enum: ['always'], description: 'Requires that all returned promises be awaited.', + enum: ['always'], }, { type: 'string', - enum: ['error-handling-correctness-only'], description: 'In error-handling contexts, the rule enforces that returned promises must be awaited. In ordinary contexts, the rule does not enforce any particular behavior around whether returned promises are awaited.', + enum: ['error-handling-correctness-only'], }, { type: 'string', - enum: ['in-try-catch'], description: 'In error-handling contexts, the rule enforces that returned promises must be awaited. In ordinary contexts, the rule enforces that returned promises _must not_ be awaited.', + enum: ['in-try-catch'], }, { type: 'string', - enum: ['never'], description: 'Disallows awaiting any returned promises.', + enum: ['never'], }, ], }, @@ -126,7 +127,7 @@ export default createRule({ // if it's a using/await using declaration, and it comes _before_ the // node we're checking, it affects control flow for that node. if ( - ['using', 'await using'].includes(declarationNode.kind) && + ['await using', 'using'].includes(declarationNode.kind) && declaratorNode.range[1] < node.range[0] ) { return true; @@ -167,13 +168,9 @@ export default createRule({ return false; } - const { tryStatement, block } = tryAncestorResult; + const { block, tryStatement } = tryAncestorResult; switch (block) { - case 'try': - // Try blocks are always followed by either a catch or finally, - // so exceptions thrown here always affect control flow. - return true; case 'catch': // Exceptions thrown in catch blocks followed by a finally block affect // control flow. @@ -185,6 +182,10 @@ export default createRule({ return affectsExplicitErrorHandling(tryStatement); case 'finally': return affectsExplicitErrorHandling(tryStatement); + case 'try': + // Try blocks are always followed by either a catch or finally, + // so exceptions thrown here always affect control flow. + return true; default: { const __never: never = block; throw new Error(`Unexpected block type: ${String(__never)}`); @@ -193,8 +194,8 @@ export default createRule({ } interface FindContainingTryStatementResult { + block: 'catch' | 'finally' | 'try'; tryStatement: ts.TryStatement; - block: 'try' | 'catch' | 'finally'; } /** @@ -211,7 +212,7 @@ export default createRule({ while (ancestor && !ts.isFunctionLike(ancestor)) { if (ts.isTryStatement(ancestor)) { - let block: 'try' | 'catch' | 'finally' | undefined; + let block: 'catch' | 'finally' | 'try' | undefined; if (child === ancestor.tryBlock) { block = 'try'; } else if (child === ancestor.catchClause) { @@ -221,11 +222,11 @@ export default createRule({ } return { - tryStatement: ancestor, block: nullThrows( block, 'Child of a try statement must be a try block, catch clause, or finally block', ), + tryStatement: ancestor, }; } child = ancestor; @@ -307,16 +308,14 @@ export default createRule({ if (!isThenable) { if (isAwait) { - // any/unknown could be thenable; do not auto-fix - const useAutoFix = !(isTypeAnyType(type) || isTypeUnknownType(type)); - + // any/unknown could be thenable; do not enforce whether they are `await`ed. + if (isTypeAnyType(type) || isTypeUnknownType(type)) { + return; + } context.report({ - messageId: 'nonPromiseAwait', node, - ...fixOrSuggest(useAutoFix, { - messageId: 'nonPromiseAwait', - fix: fixer => removeAwait(fixer, node), - }), + messageId: 'nonPromiseAwait', + fix: fixer => removeAwait(fixer, node), }); } return; @@ -336,13 +335,11 @@ export default createRule({ : ruleConfiguration.ordinaryContext; switch (shouldAwaitInCurrentContext) { - case "don't-care": - break; case 'await': if (!isAwait) { context.report({ - messageId: 'requiredPromiseAwait', node, + messageId: 'requiredPromiseAwait', ...fixOrSuggest(useAutoFix, { messageId: 'requiredPromiseAwaitSuggestion', fix: fixer => @@ -355,11 +352,13 @@ export default createRule({ }); } break; + case "don't-care": + break; case 'no-await': if (isAwait) { context.report({ - messageId: 'disallowedPromiseAwait', node, + messageId: 'disallowedPromiseAwait', ...fixOrSuggest(useAutoFix, { messageId: 'disallowedPromiseAwaitSuggestion', fix: fixer => removeAwait(fixer, node), @@ -383,13 +382,13 @@ export default createRule({ } return { - FunctionDeclaration: enterFunction, - FunctionExpression: enterFunction, ArrowFunctionExpression: enterFunction, + 'ArrowFunctionExpression:exit': exitFunction, + FunctionDeclaration: enterFunction, 'FunctionDeclaration:exit': exitFunction, + FunctionExpression: enterFunction, 'FunctionExpression:exit': exitFunction, - 'ArrowFunctionExpression:exit': exitFunction, // executes after less specific handler, so exitFunction is called 'ArrowFunctionExpression[async = true]:exit'( @@ -416,34 +415,34 @@ export default createRule({ }, }); -type WhetherToAwait = 'await' | 'no-await' | "don't-care"; +type WhetherToAwait = "don't-care" | 'await' | 'no-await'; interface RuleConfiguration { - ordinaryContext: WhetherToAwait; errorHandlingContext: WhetherToAwait; + ordinaryContext: WhetherToAwait; } function getConfiguration(option: Option): RuleConfiguration { switch (option) { case 'always': return { - ordinaryContext: 'await', errorHandlingContext: 'await', - }; - case 'never': - return { - ordinaryContext: 'no-await', - errorHandlingContext: 'no-await', + ordinaryContext: 'await', }; case 'error-handling-correctness-only': return { - ordinaryContext: "don't-care", errorHandlingContext: 'await', + ordinaryContext: "don't-care", }; case 'in-try-catch': return { - ordinaryContext: 'no-await', errorHandlingContext: 'await', + ordinaryContext: 'no-await', + }; + case 'never': + return { + errorHandlingContext: 'no-await', + ordinaryContext: 'no-await', }; } } diff --git a/packages/eslint-plugin/src/rules/sort-type-constituents.ts b/packages/eslint-plugin/src/rules/sort-type-constituents.ts index de87dc2aa962..d6a2779ba8f4 100644 --- a/packages/eslint-plugin/src/rules/sort-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/sort-type-constituents.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, getEnumNames, typeNodeRequiresParentheses } from '../util'; @@ -106,9 +107,9 @@ function caseSensitiveSort(a: string, b: string): number { export type Options = [ { + caseSensitive?: boolean; checkIntersections?: boolean; checkUnions?: boolean; - caseSensitive?: boolean; groupOrder?: string[]; }, ]; @@ -117,12 +118,8 @@ export type MessageIds = 'notSorted' | 'notSortedNamed' | 'suggestFix'; export default createRule({ name: 'sort-type-constituents', meta: { - deprecated: true, - replacedBy: [ - 'perfectionist/sort-intersection-types', - 'perfectionist/sort-union-types', - ], type: 'suggestion', + deprecated: true, docs: { description: 'Enforce constituents of a type union/intersection to be sorted alphabetically', @@ -134,26 +131,30 @@ export default createRule({ notSortedNamed: '{{type}} type {{name}} constituents must be sorted.', suggestFix: 'Sort constituents of type (removes all comments).', }, + replacedBy: [ + 'perfectionist/sort-intersection-types', + 'perfectionist/sort-union-types', + ], schema: [ { type: 'object', additionalProperties: false, properties: { - checkIntersections: { - description: 'Whether to check intersection types.', + caseSensitive: { type: 'boolean', + description: 'Whether to sort using case sensitive sorting.', }, - checkUnions: { - description: 'Whether to check union types.', + checkIntersections: { type: 'boolean', + description: 'Whether to check intersection types.', }, - caseSensitive: { - description: 'Whether to sort using case sensitive sorting.', + checkUnions: { type: 'boolean', + description: 'Whether to check union types.', }, groupOrder: { - description: 'Ordering of the groups.', type: 'array', + description: 'Ordering of the groups.', items: { type: 'string', enum: getEnumNames(Group), @@ -165,9 +166,9 @@ export default createRule({ }, defaultOptions: [ { + caseSensitive: false, checkIntersections: true, checkUnions: true, - caseSensitive: false, groupOrder: [ Group.named, Group.keyword, @@ -186,11 +187,11 @@ export default createRule({ ], create( context, - [{ checkIntersections, checkUnions, caseSensitive, groupOrder }], + [{ caseSensitive, checkIntersections, checkUnions, groupOrder }], ) { const collator = new Intl.Collator('en', { - sensitivity: 'base', numeric: true, + sensitivity: 'base', }); function checkSorting( @@ -199,8 +200,8 @@ export default createRule({ const sourceOrder = node.types.map(type => { const group = groupOrder?.indexOf(getGroup(type)) ?? -1; return { - group: group === -1 ? Number.MAX_SAFE_INTEGER : group, node: type, + group: group === -1 ? Number.MAX_SAFE_INTEGER : group, text: context.sourceCode.getText(type), }; }); diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index 7adbad1e525d..1933d2899c14 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -2,6 +2,7 @@ import type { ParserServicesWithTypeInformation, TSESTree, } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -17,15 +18,15 @@ import { findTruthinessAssertedArgument } from '../util/assertionFunctionUtils'; export type Options = [ { - allowString?: boolean; - allowNumber?: boolean; - allowNullableObject?: boolean; + allowAny?: boolean; allowNullableBoolean?: boolean; - allowNullableString?: boolean; - allowNullableNumber?: boolean; allowNullableEnum?: boolean; - allowAny?: boolean; + allowNullableNumber?: boolean; + allowNullableObject?: boolean; + allowNullableString?: boolean; + allowNumber?: boolean; allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean; + allowString?: boolean; }, ]; @@ -58,132 +59,132 @@ export default createRule({ name: 'strict-boolean-expressions', meta: { type: 'suggestion', - fixable: 'code', - hasSuggestions: true, docs: { description: 'Disallow certain types in boolean expressions', requiresTypeChecking: true, }, + fixable: 'code', + hasSuggestions: true, + messages: { + conditionErrorAny: + 'Unexpected any value in conditional. ' + + 'An explicit comparison or type cast is required.', + conditionErrorNullableBoolean: + 'Unexpected nullable boolean value in conditional. ' + + 'Please handle the nullish case explicitly.', + conditionErrorNullableEnum: + 'Unexpected nullable enum value in conditional. ' + + 'Please handle the nullish/zero/NaN cases explicitly.', + conditionErrorNullableNumber: + 'Unexpected nullable number value in conditional. ' + + 'Please handle the nullish/zero/NaN cases explicitly.', + conditionErrorNullableObject: + 'Unexpected nullable object value in conditional. ' + + 'An explicit null check is required.', + conditionErrorNullableString: + 'Unexpected nullable string value in conditional. ' + + 'Please handle the nullish/empty cases explicitly.', + conditionErrorNullish: + 'Unexpected nullish value in conditional. ' + + 'The condition is always false.', + conditionErrorNumber: + 'Unexpected number value in conditional. ' + + 'An explicit zero/NaN check is required.', + conditionErrorObject: + 'Unexpected object value in conditional. ' + + 'The condition is always true.', + conditionErrorOther: + 'Unexpected value in conditional. ' + + 'A boolean expression is required.', + conditionErrorString: + 'Unexpected string value in conditional. ' + + 'An explicit empty string check is required.', + conditionFixCastBoolean: + 'Explicitly cast value to a boolean (`Boolean(value)`)', + + conditionFixCompareEmptyString: + 'Change condition to check for empty string (`value !== ""`)', + conditionFixCompareFalse: + 'Change condition to check if false (`value === false`)', + conditionFixCompareNaN: + 'Change condition to check for NaN (`!Number.isNaN(value)`)', + conditionFixCompareNullish: + 'Change condition to check for null/undefined (`value != null`)', + conditionFixCompareStringLength: + "Change condition to check string's length (`value.length !== 0`)", + conditionFixCompareTrue: + 'Change condition to check if true (`value === true`)', + conditionFixCompareZero: + 'Change condition to check for 0 (`value !== 0`)', + conditionFixDefaultEmptyString: + 'Explicitly treat nullish value the same as an empty string (`value ?? ""`)', + conditionFixDefaultFalse: + 'Explicitly treat nullish value the same as false (`value ?? false`)', + conditionFixDefaultZero: + 'Explicitly treat nullish value the same as 0 (`value ?? 0`)', + noStrictNullCheck: + 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', + }, schema: [ { type: 'object', + additionalProperties: false, properties: { - allowString: { - description: 'Whether to allow `string` in a boolean context.', - type: 'boolean', - }, - allowNumber: { - description: 'Whether to allow `number` in a boolean context.', - type: 'boolean', - }, - allowNullableObject: { - description: - 'Whether to allow nullable `object`s in a boolean context.', + allowAny: { type: 'boolean', + description: 'Whether to allow `any` in a boolean context.', }, allowNullableBoolean: { + type: 'boolean', description: 'Whether to allow nullable `boolean`s in a boolean context.', - type: 'boolean', }, - allowNullableString: { - description: - 'Whether to allow nullable `string`s in a boolean context.', + allowNullableEnum: { type: 'boolean', + description: + 'Whether to allow nullable `enum`s in a boolean context.', }, allowNullableNumber: { + type: 'boolean', description: 'Whether to allow nullable `number`s in a boolean context.', - type: 'boolean', }, - allowNullableEnum: { + allowNullableObject: { + type: 'boolean', description: - 'Whether to allow nullable `enum`s in a boolean context.', + 'Whether to allow nullable `object`s in a boolean context.', + }, + allowNullableString: { type: 'boolean', + description: + 'Whether to allow nullable `string`s in a boolean context.', }, - allowAny: { - description: 'Whether to allow `any` in a boolean context.', + allowNumber: { type: 'boolean', + description: 'Whether to allow `number` in a boolean context.', }, allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: { type: 'boolean', }, + allowString: { + type: 'boolean', + description: 'Whether to allow `string` in a boolean context.', + }, }, - additionalProperties: false, }, ], - messages: { - conditionErrorOther: - 'Unexpected value in conditional. ' + - 'A boolean expression is required.', - conditionErrorAny: - 'Unexpected any value in conditional. ' + - 'An explicit comparison or type cast is required.', - conditionErrorNullish: - 'Unexpected nullish value in conditional. ' + - 'The condition is always false.', - conditionErrorNullableBoolean: - 'Unexpected nullable boolean value in conditional. ' + - 'Please handle the nullish case explicitly.', - conditionErrorString: - 'Unexpected string value in conditional. ' + - 'An explicit empty string check is required.', - conditionErrorNullableString: - 'Unexpected nullable string value in conditional. ' + - 'Please handle the nullish/empty cases explicitly.', - conditionErrorNumber: - 'Unexpected number value in conditional. ' + - 'An explicit zero/NaN check is required.', - conditionErrorNullableNumber: - 'Unexpected nullable number value in conditional. ' + - 'Please handle the nullish/zero/NaN cases explicitly.', - conditionErrorObject: - 'Unexpected object value in conditional. ' + - 'The condition is always true.', - conditionErrorNullableObject: - 'Unexpected nullable object value in conditional. ' + - 'An explicit null check is required.', - conditionErrorNullableEnum: - 'Unexpected nullable enum value in conditional. ' + - 'Please handle the nullish/zero/NaN cases explicitly.', - noStrictNullCheck: - 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.', - - conditionFixDefaultFalse: - 'Explicitly treat nullish value the same as false (`value ?? false`)', - conditionFixDefaultEmptyString: - 'Explicitly treat nullish value the same as an empty string (`value ?? ""`)', - conditionFixDefaultZero: - 'Explicitly treat nullish value the same as 0 (`value ?? 0`)', - conditionFixCompareNullish: - 'Change condition to check for null/undefined (`value != null`)', - conditionFixCastBoolean: - 'Explicitly cast value to a boolean (`Boolean(value)`)', - conditionFixCompareTrue: - 'Change condition to check if true (`value === true`)', - conditionFixCompareFalse: - 'Change condition to check if false (`value === false`)', - conditionFixCompareStringLength: - "Change condition to check string's length (`value.length !== 0`)", - conditionFixCompareEmptyString: - 'Change condition to check for empty string (`value !== ""`)', - conditionFixCompareZero: - 'Change condition to check for 0 (`value !== 0`)', - conditionFixCompareNaN: - 'Change condition to check for NaN (`!Number.isNaN(value)`)', - }, }, defaultOptions: [ { - allowString: true, - allowNumber: true, - allowNullableObject: true, + allowAny: false, allowNullableBoolean: false, - allowNullableString: false, - allowNullableNumber: false, allowNullableEnum: false, - allowAny: false, + allowNullableNumber: false, + allowNullableObject: true, + allowNullableString: false, + allowNumber: true, allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, + allowString: true, }, ], create(context, [options]) { @@ -202,8 +203,8 @@ export default createRule({ ) { context.report({ loc: { - start: { line: 0, column: 0 }, - end: { line: 0, column: 0 }, + start: { column: 0, line: 0 }, + end: { column: 0, line: 0 }, }, messageId: 'noStrictNullCheck', }); @@ -212,14 +213,14 @@ export default createRule({ const traversedNodes = new Set(); return { + CallExpression: traverseCallExpression, ConditionalExpression: traverseTestExpression, DoWhileStatement: traverseTestExpression, ForStatement: traverseTestExpression, IfStatement: traverseTestExpression, - WhileStatement: traverseTestExpression, 'LogicalExpression[operator!="??"]': traverseLogicalExpression, 'UnaryExpression[operator="!"]': traverseUnaryLogicalExpression, - CallExpression: traverseCallExpression, + WhileStatement: traverseTestExpression, }; type TestExpression = @@ -357,17 +358,17 @@ export default createRule({ { messageId: 'conditionFixDefaultFalse', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} ?? false`, }), }, { messageId: 'conditionFixCompareFalse', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code} === false`, }), }, @@ -382,16 +383,16 @@ export default createRule({ { messageId: 'conditionFixDefaultFalse', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} ?? false`, }), }, { messageId: 'conditionFixCompareTrue', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} === true`, }), }, @@ -422,27 +423,27 @@ export default createRule({ { messageId: 'conditionFixCompareStringLength', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code}.length === 0`, }), }, { messageId: 'conditionFixCompareEmptyString', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code} === ""`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `!Boolean(${code})`, }), }, @@ -457,24 +458,24 @@ export default createRule({ { messageId: 'conditionFixCompareStringLength', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code}.length > 0`, }), }, { messageId: 'conditionFixCompareEmptyString', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} !== ""`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `Boolean(${code})`, }), }, @@ -497,26 +498,26 @@ export default createRule({ { messageId: 'conditionFixCompareNullish', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code} == null`, }), }, { messageId: 'conditionFixDefaultEmptyString', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} ?? ""`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `!Boolean(${code})`, }), }, @@ -531,24 +532,24 @@ export default createRule({ { messageId: 'conditionFixCompareNullish', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} != null`, }), }, { messageId: 'conditionFixDefaultEmptyString', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} ?? ""`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `Boolean(${code})`, }), }, @@ -569,9 +570,9 @@ export default createRule({ node, messageId: 'conditionErrorNumber', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code} === 0`, }), }); @@ -581,8 +582,8 @@ export default createRule({ node, messageId: 'conditionErrorNumber', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} > 0`, }), }); @@ -596,9 +597,9 @@ export default createRule({ { messageId: 'conditionFixCompareZero', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, // TODO: we have to compare to 0n if the type is bigint wrap: code => `${code} === 0`, }), @@ -607,18 +608,18 @@ export default createRule({ // TODO: don't suggest this for bigint because it can't be NaN messageId: 'conditionFixCompareNaN', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `Number.isNaN(${code})`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `!Boolean(${code})`, }), }, @@ -633,24 +634,24 @@ export default createRule({ { messageId: 'conditionFixCompareZero', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} !== 0`, }), }, { messageId: 'conditionFixCompareNaN', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `!Number.isNaN(${code})`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `Boolean(${code})`, }), }, @@ -673,26 +674,26 @@ export default createRule({ { messageId: 'conditionFixCompareNullish', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code} == null`, }), }, { messageId: 'conditionFixDefaultZero', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} ?? 0`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `!Boolean(${code})`, }), }, @@ -707,24 +708,24 @@ export default createRule({ { messageId: 'conditionFixCompareNullish', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} != null`, }), }, { messageId: 'conditionFixDefaultZero', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} ?? 0`, }), }, { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `Boolean(${code})`, }), }, @@ -754,9 +755,9 @@ export default createRule({ { messageId: 'conditionFixCompareNullish', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code} == null`, }), }, @@ -771,8 +772,8 @@ export default createRule({ { messageId: 'conditionFixCompareNullish', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} != null`, }), }, @@ -801,9 +802,9 @@ export default createRule({ node, messageId: 'conditionErrorNullableEnum', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node: node.parent, innerNode: node, + sourceCode: context.sourceCode, wrap: code => `${code} == null`, }), }); @@ -812,8 +813,8 @@ export default createRule({ node, messageId: 'conditionErrorNullableEnum', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `${code} != null`, }), }); @@ -832,8 +833,8 @@ export default createRule({ { messageId: 'conditionFixCastBoolean', fix: getWrappingFixer({ - sourceCode: context.sourceCode, node, + sourceCode: context.sourceCode, wrap: code => `Boolean(${code})`, }), }, diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 1452d7de6cdb..c5ebc2970175 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -14,10 +15,10 @@ import { } from '../util'; interface SwitchMetadata { - readonly symbolName: string | undefined; + readonly containsNonLiteralType: boolean; readonly defaultCase: TSESTree.SwitchCase | undefined; readonly missingLiteralBranchTypes: ts.Type[]; - readonly containsNonLiteralType: boolean; + readonly symbolName: string | undefined; } type Options = [ @@ -40,9 +41,9 @@ type Options = [ ]; type MessageIds = - | 'switchIsNotExhaustive' + | 'addMissingCases' | 'dangerousDefaultCase' - | 'addMissingCases'; + | 'switchIsNotExhaustive'; export default createRule({ name: 'switch-exhaustiveness-check', @@ -53,29 +54,29 @@ export default createRule({ requiresTypeChecking: true, }, hasSuggestions: true, + messages: { + addMissingCases: 'Add branches for missing cases.', + dangerousDefaultCase: + 'The switch statement is exhaustive, so the default case is unnecessary.', + switchIsNotExhaustive: + 'Switch is not exhaustive. Cases not matched: {{missingBranches}}', + }, schema: [ { type: 'object', + additionalProperties: false, properties: { allowDefaultCaseForExhaustiveSwitch: { - description: `If 'true', allow 'default' cases on switch statements with exhaustive cases.`, type: 'boolean', + description: `If 'true', allow 'default' cases on switch statements with exhaustive cases.`, }, requireDefaultForNonUnion: { - description: `If 'true', require a 'default' clause for switches on non-union types.`, type: 'boolean', + description: `If 'true', require a 'default' clause for switches on non-union types.`, }, }, - additionalProperties: false, }, ], - messages: { - switchIsNotExhaustive: - 'Switch is not exhaustive. Cases not matched: {{missingBranches}}', - dangerousDefaultCase: - 'The switch statement is exhaustive, so the default case is unnecessary.', - addMissingCases: 'Add branches for missing cases.', - }, }, defaultOptions: [ { @@ -141,10 +142,10 @@ export default createRule({ } return { - symbolName, - missingLiteralBranchTypes, - defaultCase, containsNonLiteralType, + defaultCase, + missingLiteralBranchTypes, + symbolName, }; } @@ -152,7 +153,7 @@ export default createRule({ node: TSESTree.SwitchStatement, switchMetadata: SwitchMetadata, ): void { - const { missingLiteralBranchTypes, symbolName, defaultCase } = + const { defaultCase, missingLiteralBranchTypes, symbolName } = switchMetadata; // We only trigger the rule if a `default` case does not exist, since that @@ -275,7 +276,7 @@ export default createRule({ return; } - const { missingLiteralBranchTypes, defaultCase, containsNonLiteralType } = + const { containsNonLiteralType, defaultCase, missingLiteralBranchTypes } = switchMetadata; if ( @@ -298,7 +299,7 @@ export default createRule({ return; } - const { defaultCase, containsNonLiteralType } = switchMetadata; + const { containsNonLiteralType, defaultCase } = switchMetadata; if (containsNonLiteralType && defaultCase === undefined) { context.report({ diff --git a/packages/eslint-plugin/src/rules/triple-slash-reference.ts b/packages/eslint-plugin/src/rules/triple-slash-reference.ts index 7512f8ce0778..d7908e7273b0 100644 --- a/packages/eslint-plugin/src/rules/triple-slash-reference.ts +++ b/packages/eslint-plugin/src/rules/triple-slash-reference.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -28,27 +29,27 @@ export default createRule({ schema: [ { type: 'object', + additionalProperties: false, properties: { lib: { + type: 'string', description: 'What to enforce for `/// ` references.', - type: 'string', enum: ['always', 'never'], }, path: { + type: 'string', description: 'What to enforce for `/// ` references.', - type: 'string', enum: ['always', 'never'], }, types: { + type: 'string', description: 'What to enforce for `/// ` references.', - type: 'string', enum: ['always', 'never', 'prefer-import'], }, }, - additionalProperties: false, }, ], }, @@ -86,15 +87,6 @@ export default createRule({ hasMatchingReference(node.source); } }, - TSImportEqualsDeclaration(node): void { - if (programNode) { - const reference = node.moduleReference; - - if (reference.type === AST_NODE_TYPES.TSExternalModuleReference) { - hasMatchingReference(reference.expression as TSESTree.Literal); - } - } - }, Program(node): void { if (lib === 'always' && path === 'always' && types === 'always') { return; @@ -132,6 +124,15 @@ export default createRule({ } }); }, + TSImportEqualsDeclaration(node): void { + if (programNode) { + const reference = node.moduleReference; + + if (reference.type === AST_NODE_TYPES.TSExternalModuleReference) { + hasMatchingReference(reference.expression as TSESTree.Literal); + } + } + }, }; }, }); diff --git a/packages/eslint-plugin/src/rules/typedef.ts b/packages/eslint-plugin/src/rules/typedef.ts index 5853c79dfff9..7a18ac37b572 100644 --- a/packages/eslint-plugin/src/rules/typedef.ts +++ b/packages/eslint-plugin/src/rules/typedef.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule } from '../util'; @@ -21,6 +22,7 @@ type MessageIds = 'expectedTypedef' | 'expectedTypedefNamed'; export default createRule<[Options], MessageIds>({ name: 'typedef', meta: { + type: 'suggestion', docs: { description: 'Require type annotations in certain places', }, @@ -34,49 +36,48 @@ export default createRule<[Options], MessageIds>({ additionalProperties: false, properties: { [OptionKeys.ArrayDestructuring]: { + type: 'boolean', description: 'Whether to enforce type annotations on variables declared using array destructuring.', - type: 'boolean', }, [OptionKeys.ArrowParameter]: { + type: 'boolean', description: 'Whether to enforce type annotations for parameters of arrow functions.', - type: 'boolean', }, [OptionKeys.MemberVariableDeclaration]: { + type: 'boolean', description: 'Whether to enforce type annotations on member variables of classes.', - type: 'boolean', }, [OptionKeys.ObjectDestructuring]: { + type: 'boolean', description: 'Whether to enforce type annotations on variables declared using object destructuring.', - type: 'boolean', }, [OptionKeys.Parameter]: { + type: 'boolean', description: 'Whether to enforce type annotations for parameters of functions and methods.', - type: 'boolean', }, [OptionKeys.PropertyDeclaration]: { + type: 'boolean', description: 'Whether to enforce type annotations for properties of interfaces and types.', - type: 'boolean', }, [OptionKeys.VariableDeclaration]: { + type: 'boolean', description: 'Whether to enforce type annotations for variable declarations, excluding array and object destructuring.', - type: 'boolean', }, [OptionKeys.VariableDeclarationIgnoreFunction]: { + type: 'boolean', description: 'Whether to ignore variable declarations for non-arrow and arrow functions.', - type: 'boolean', }, }, }, ], - type: 'suggestion', }, defaultOptions: [ { diff --git a/packages/eslint-plugin/src/rules/unbound-method.ts b/packages/eslint-plugin/src/rules/unbound-method.ts index 6e2c34d94b72..37e8d0868e25 100644 --- a/packages/eslint-plugin/src/rules/unbound-method.ts +++ b/packages/eslint-plugin/src/rules/unbound-method.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; @@ -105,6 +106,7 @@ const BASE_MESSAGE = export default createRule({ name: 'unbound-method', meta: { + type: 'problem', docs: { description: 'Enforce unbound methods are called with their expected scope', @@ -118,17 +120,16 @@ export default createRule({ schema: [ { type: 'object', + additionalProperties: false, properties: { ignoreStatic: { + type: 'boolean', description: 'Whether to skip checking whether `static` methods are correctly bound.', - type: 'boolean', }, }, - additionalProperties: false, }, ], - type: 'problem', }, defaultOptions: [ { @@ -153,11 +154,11 @@ export default createRule({ ); if (dangerous) { context.report({ + node, messageId: firstParamIsThis === false ? 'unboundWithoutThisAnnotation' : 'unbound', - node, }); return true; } @@ -337,9 +338,9 @@ function checkIfMethod( function checkMethod( valueDeclaration: + | ts.FunctionExpression | ts.MethodDeclaration - | ts.MethodSignature - | ts.FunctionExpression, + | ts.MethodSignature, ignoreStatic: boolean, ): CheckMethodResult { const firstParam = valueDeclaration.parameters.at(0); @@ -389,10 +390,10 @@ function isSafeUse(node: TSESTree.Node): boolean { // the first case is safe for obvious // reasons. The second one is also fine // since we're returning something falsy - return ['typeof', '!', 'void', 'delete'].includes(parent.operator); + return ['!', 'delete', 'typeof', 'void'].includes(parent.operator); case AST_NODE_TYPES.BinaryExpression: - return ['instanceof', '==', '!=', '===', '!=='].includes(parent.operator); + return ['!=', '!==', '==', '===', 'instanceof'].includes(parent.operator); case AST_NODE_TYPES.AssignmentExpression: return ( diff --git a/packages/eslint-plugin/src/rules/unified-signatures.ts b/packages/eslint-plugin/src/rules/unified-signatures.ts index ee012cceb0ed..1b19bff45e73 100644 --- a/packages/eslint-plugin/src/rules/unified-signatures.ts +++ b/packages/eslint-plugin/src/rules/unified-signatures.ts @@ -1,18 +1,20 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { Equal } from '../util'; + import { arraysAreEqual, createRule, nullThrows } from '../util'; interface Failure { - unify: Unify; only2: boolean; + unify: Unify; } type Unify = | { - kind: 'extra-parameter'; extraParameter: TSESTree.Parameter; + kind: 'extra-parameter'; otherSignature: SignatureDefinition; } | { @@ -65,13 +67,13 @@ type Options = [ export default createRule({ name: 'unified-signatures', meta: { + type: 'suggestion', docs: { description: 'Disallow two overloads that could be unified into one with a union or an optional/rest parameter', // too opinionated to be recommended recommended: 'strict', }, - type: 'suggestion', messages: { omittingRestParameter: '{{failureStringStart}} with a rest parameter.', omittingSingleParameter: @@ -81,15 +83,15 @@ export default createRule({ }, schema: [ { + type: 'object', additionalProperties: false, properties: { ignoreDifferentlyNamedParameters: { + type: 'boolean', description: 'Whether two parameters with different names at the same index should be considered different even if their types are the same.', - type: 'boolean', }, }, - type: 'object', }, ], }, @@ -114,7 +116,7 @@ export default createRule({ function addFailures(failures: Failure[]): void { for (const failure of failures) { - const { unify, only2 } = failure; + const { only2, unify } = failure; switch (unify.kind) { case 'single-parameter-difference': { const { p0, p1 } = unify; @@ -129,6 +131,7 @@ export default createRule({ context.report({ loc: p1.loc, + node: p1, messageId: 'singleParameterDifference', data: { failureStringStart: failureStringStart(lineOfOtherOverload), @@ -139,7 +142,6 @@ export default createRule({ typeAnnotation1?.typeAnnotation, ), }, - node: p1, }); break; } @@ -151,6 +153,7 @@ export default createRule({ context.report({ loc: extraParameter.loc, + node: extraParameter, messageId: extraParameter.type === AST_NODE_TYPES.RestElement ? 'omittingRestParameter' @@ -158,7 +161,6 @@ export default createRule({ data: { failureStringStart: failureStringStart(lineOfOtherOverload), }, - node: extraParameter, }); } } @@ -182,7 +184,7 @@ export default createRule({ isTypeParameter, ); if (unify !== undefined) { - result.push({ unify, only2: overloads.length === 2 }); + result.push({ only2: overloads.length === 2, unify }); } }); } @@ -546,40 +548,40 @@ export default createRule({ //---------------------------------------------------------------------- return { - Program: createScope, - TSModuleBlock: createScope, - TSInterfaceDeclaration(node): void { + ClassDeclaration(node): void { createScope(node.body, node.typeParameters); }, - ClassDeclaration(node): void { + Program: createScope, + TSInterfaceDeclaration(node): void { createScope(node.body, node.typeParameters); }, + TSModuleBlock: createScope, TSTypeLiteral: createScope, // collect overloads - TSDeclareFunction(node): void { - const exportingNode = getExportingNode(node); - addOverload(node, node.id?.name ?? exportingNode?.type, exportingNode); - }, - TSCallSignatureDeclaration: addOverload, - TSConstructSignatureDeclaration: addOverload, - TSMethodSignature: addOverload, - TSAbstractMethodDefinition(node): void { + MethodDefinition(node): void { if (!node.value.body) { addOverload(node); } }, - MethodDefinition(node): void { + TSAbstractMethodDefinition(node): void { if (!node.value.body) { addOverload(node); } }, + TSCallSignatureDeclaration: addOverload, + TSConstructSignatureDeclaration: addOverload, + TSDeclareFunction(node): void { + const exportingNode = getExportingNode(node); + addOverload(node, node.id?.name ?? exportingNode?.type, exportingNode); + }, + TSMethodSignature: addOverload, // validate scopes + 'ClassDeclaration:exit': checkScope, 'Program:exit': checkScope, - 'TSModuleBlock:exit': checkScope, 'TSInterfaceDeclaration:exit': checkScope, - 'ClassDeclaration:exit': checkScope, + 'TSModuleBlock:exit': checkScope, 'TSTypeLiteral:exit': checkScope, }; }, diff --git a/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts b/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts index a6060cd3f00e..a7d2c53b1a2a 100644 --- a/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts +++ b/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts @@ -1,9 +1,10 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { ReportDescriptor } from '@typescript-eslint/utils/ts-eslint'; -import * as tsutils from 'ts-api-utils'; import type * as ts from 'typescript'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import * as tsutils from 'ts-api-utils'; + import { createRule, getParserServices, @@ -14,13 +15,13 @@ import { } from '../util'; type MessageIds = + | 'addUnknownRestTypeAnnotationSuggestion' + | 'addUnknownTypeAnnotationSuggestion' | 'useUnknown' | 'useUnknownArrayDestructuringPattern' | 'useUnknownObjectDestructuringPattern' - | 'addUnknownTypeAnnotationSuggestion' - | 'addUnknownRestTypeAnnotationSuggestion' - | 'wrongTypeAnnotationSuggestion' - | 'wrongRestTypeAnnotationSuggestion'; + | 'wrongRestTypeAnnotationSuggestion' + | 'wrongTypeAnnotationSuggestion'; const useUnknownMessageBase = 'Prefer the safe `: unknown` for a `{{method}}`{{append}} callback variable.'; @@ -28,37 +29,37 @@ const useUnknownMessageBase = export default createRule<[], MessageIds>({ name: 'use-unknown-in-catch-callback-variable', meta: { + type: 'suggestion', docs: { description: 'Enforce typing arguments in Promise rejection callbacks as `unknown`', - requiresTypeChecking: true, recommended: 'strict', + requiresTypeChecking: true, }, - type: 'suggestion', + fixable: 'code', + hasSuggestions: true, messages: { + addUnknownRestTypeAnnotationSuggestion: + 'Add an explicit `: [unknown]` type annotation to the rejection callback rest variable.', + addUnknownTypeAnnotationSuggestion: + 'Add an explicit `: unknown` type annotation to the rejection callback variable.', useUnknown: useUnknownMessageBase, useUnknownArrayDestructuringPattern: `${useUnknownMessageBase} The thrown error may not be iterable.`, useUnknownObjectDestructuringPattern: `${ useUnknownMessageBase } The thrown error may be nullable, or may not have the expected shape.`, - addUnknownTypeAnnotationSuggestion: - 'Add an explicit `: unknown` type annotation to the rejection callback variable.', - addUnknownRestTypeAnnotationSuggestion: - 'Add an explicit `: [unknown]` type annotation to the rejection callback rest variable.', - wrongTypeAnnotationSuggestion: - 'Change existing type annotation to `: unknown`.', wrongRestTypeAnnotationSuggestion: 'Change existing type annotation to `: [unknown]`.', + wrongTypeAnnotationSuggestion: + 'Change existing type annotation to `: unknown`.', }, - fixable: 'code', schema: [], - hasSuggestions: true, }, defaultOptions: [], create(context) { - const { program, esTreeNodeToTSNodeMap } = getParserServices(context); + const { esTreeNodeToTSNodeMap, program } = getParserServices(context); const checker = program.getTypeChecker(); function isFlaggableHandlerType(type: ts.Type): boolean { @@ -114,7 +115,7 @@ export default createRule<[], MessageIds>({ */ function refineReportIfPossible( argument: TSESTree.Expression, - ): undefined | Partial> { + ): Partial> | undefined { // Only know how to be helpful if a function literal has been provided. if ( !( @@ -238,12 +239,12 @@ export default createRule<[], MessageIds>({ const promiseMethodInfo = ( [ - { method: 'catch', append: '', argIndexToCheck: 0 }, - { method: 'then', append: ' rejection', argIndexToCheck: 1 }, + { append: '', argIndexToCheck: 0, method: 'catch' }, + { append: ' rejection', argIndexToCheck: 1, method: 'then' }, ] satisfies { - method: string; append: string; argIndexToCheck: number; + method: string; }[] ).find(({ method }) => staticMemberAccessKey === method); if (!promiseMethodInfo) { diff --git a/packages/eslint-plugin/src/util/assertionFunctionUtils.ts b/packages/eslint-plugin/src/util/assertionFunctionUtils.ts index c561d1c59df8..5929ff4d8c82 100644 --- a/packages/eslint-plugin/src/util/assertionFunctionUtils.ts +++ b/packages/eslint-plugin/src/util/assertionFunctionUtils.ts @@ -2,6 +2,7 @@ import type { ParserServicesWithTypeInformation, TSESTree, } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; @@ -44,7 +45,7 @@ export function findTruthinessAssertedArgument( return undefined; } - const { parameterIndex, kind, type } = firstTypePredicateResult; + const { kind, parameterIndex, type } = firstTypePredicateResult; if (!(kind === ts.TypePredicateKind.AssertsIdentifier && type == null)) { return undefined; } @@ -61,8 +62,8 @@ export function findTypeGuardAssertedArgument( node: TSESTree.CallExpression, ): | { - asserts: boolean; argument: TSESTree.Expression; + asserts: boolean; type: ts.Type; } | undefined { @@ -96,7 +97,7 @@ export function findTypeGuardAssertedArgument( return undefined; } - const { parameterIndex, kind, type } = typePredicateInfo; + const { kind, parameterIndex, type } = typePredicateInfo; if ( !( (kind === ts.TypePredicateKind.AssertsIdentifier || @@ -112,8 +113,8 @@ export function findTypeGuardAssertedArgument( } return { - type, - asserts: kind === ts.TypePredicateKind.AssertsIdentifier, argument: checkableArguments[parameterIndex], + asserts: kind === ts.TypePredicateKind.AssertsIdentifier, + type, }; } diff --git a/packages/eslint-plugin/src/util/astUtils.ts b/packages/eslint-plugin/src/util/astUtils.ts index daa69bc85831..c2450ae30130 100644 --- a/packages/eslint-plugin/src/util/astUtils.ts +++ b/packages/eslint-plugin/src/util/astUtils.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import * as ts from 'typescript'; import { escapeRegExp } from './escapeRegExp'; @@ -37,11 +38,11 @@ export function getNameLocationInGlobalDirectiveComment( comment.range[0] + '/*'.length + (match ? match.index + 1 : 0), ); const end = { - line: start.line, column: start.column + (match ? name.length : 1), + line: start.line, }; - return { start, end }; + return { end, start }; } // Copied from typescript https://github.com/microsoft/TypeScript/blob/42b0e3c4630c129ca39ce0df9fff5f0d1b4dd348/src/compiler/utilities.ts#L1335 diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index bc350bdee2c1..2e74a5f6a662 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -2,12 +2,13 @@ import type { ScopeManager, ScopeVariable, } from '@typescript-eslint/scope-manager'; +import type { TSESTree } from '@typescript-eslint/utils'; + import { ImplicitLibVariable, ScopeType, Visitor, } from '@typescript-eslint/scope-manager'; -import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES, ASTUtils, @@ -40,6 +41,41 @@ class UnusedVarsVisitor extends Visitor { VariableAnalysis >(); + protected ClassDeclaration = this.visitClass; + + protected ClassExpression = this.visitClass; + + protected ForInStatement = this.visitForInForOf; + + protected ForOfStatement = this.visitForInForOf; + + //#region HELPERS + + protected FunctionDeclaration = this.visitFunction; + + protected FunctionExpression = this.visitFunction; + protected MethodDefinition = this.visitSetter; + protected Property = this.visitSetter; + + protected TSCallSignatureDeclaration = this.visitFunctionTypeSignature; + + protected TSConstructorType = this.visitFunctionTypeSignature; + + protected TSConstructSignatureDeclaration = this.visitFunctionTypeSignature; + + protected TSDeclareFunction = this.visitFunctionTypeSignature; + + protected TSEmptyBodyFunctionExpression = this.visitFunctionTypeSignature; + + //#endregion HELPERS + + //#region VISITORS + // NOTE - This is a simple visitor - meaning it does not support selectors + + protected TSFunctionType = this.visitFunctionTypeSignature; + + protected TSMethodSignature = this.visitFunctionTypeSignature; + readonly #scopeManager: TSESLint.Scope.ScopeManager; private constructor(scopeManager: ScopeManager) { @@ -69,6 +105,60 @@ class UnusedVarsVisitor extends Visitor { return unusedVars; } + protected Identifier(node: TSESTree.Identifier): void { + const scope = this.getScope(node); + if ( + scope.type === TSESLint.Scope.ScopeType.function && + node.name === 'this' && + // this parameters should always be considered used as they're pseudo-parameters + 'params' in scope.block && + scope.block.params.includes(node) + ) { + this.markVariableAsUsed(node); + } + } + + protected TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { + // enum members create variables because they can be referenced within the enum, + // but they obviously aren't unused variables for the purposes of this rule. + const scope = this.getScope(node); + for (const variable of scope.variables) { + this.markVariableAsUsed(variable); + } + } + + protected TSMappedType(node: TSESTree.TSMappedType): void { + // mapped types create a variable for their type name, but it's not necessary to reference it, + // so we shouldn't consider it as unused for the purpose of this rule. + this.markVariableAsUsed(node.key); + } + + protected TSModuleDeclaration(node: TSESTree.TSModuleDeclaration): void { + // -- global augmentation can be in any file, and they do not need exports + if (node.kind === 'global') { + this.markVariableAsUsed('global', node.parent); + } + } + + protected TSParameterProperty(node: TSESTree.TSParameterProperty): void { + let identifier: TSESTree.Identifier | null = null; + switch (node.parameter.type) { + case AST_NODE_TYPES.AssignmentPattern: + if (node.parameter.left.type === AST_NODE_TYPES.Identifier) { + identifier = node.parameter.left; + } + break; + + case AST_NODE_TYPES.Identifier: + identifier = node.parameter; + break; + } + + if (identifier) { + this.markVariableAsUsed(identifier); + } + } + private collectUnusedVariables( scope: TSESLint.Scope.Scope, variables: MutableVariableAnalysis = { @@ -116,8 +206,6 @@ class UnusedVarsVisitor extends Visitor { return variables; } - //#region HELPERS - private getScope(currentNode: TSESTree.Node): TSESLint.Scope.Scope { // On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope. const inner = currentNode.type !== AST_NODE_TYPES.Program; @@ -142,7 +230,9 @@ class UnusedVarsVisitor extends Visitor { private markVariableAsUsed( variableOrIdentifier: ScopeVariable | TSESTree.Identifier, ): void; + private markVariableAsUsed(name: string, parent: TSESTree.Node): void; + private markVariableAsUsed( variableOrIdentifierOrName: ScopeVariable | TSESTree.Identifier | string, parent?: TSESTree.Node, @@ -194,49 +284,6 @@ class UnusedVarsVisitor extends Visitor { } } - private visitFunction( - node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression, - ): void { - const scope = this.getScope(node); - // skip implicit "arguments" variable - const variable = scope.set.get('arguments'); - if (variable?.defs.length === 0) { - this.markVariableAsUsed(variable); - } - } - - private visitFunctionTypeSignature( - node: - | TSESTree.TSCallSignatureDeclaration - | TSESTree.TSConstructorType - | TSESTree.TSConstructSignatureDeclaration - | TSESTree.TSDeclareFunction - | TSESTree.TSEmptyBodyFunctionExpression - | TSESTree.TSFunctionType - | TSESTree.TSMethodSignature, - ): void { - // function type signature params create variables because they can be referenced within the signature, - // but they obviously aren't unused variables for the purposes of this rule. - for (const param of node.params) { - this.visitPattern(param, name => { - this.markVariableAsUsed(name); - }); - } - } - - private visitSetter( - node: TSESTree.MethodDefinition | TSESTree.Property, - ): void { - if (node.kind === 'set') { - // ignore setter parameters because they're syntactically required to exist - for (const param of (node.value as TSESTree.FunctionLike).params) { - this.visitPattern(param, id => { - this.markVariableAsUsed(id); - }); - } - } - } - private visitForInForOf( node: TSESTree.ForInStatement | TSESTree.ForOfStatement, ): void { @@ -288,92 +335,46 @@ class UnusedVarsVisitor extends Visitor { this.markVariableAsUsed(idOrVariable); } - //#endregion HELPERS - - //#region VISITORS - // NOTE - This is a simple visitor - meaning it does not support selectors - - protected ClassDeclaration = this.visitClass; - - protected ClassExpression = this.visitClass; - - protected FunctionDeclaration = this.visitFunction; - - protected FunctionExpression = this.visitFunction; - - protected ForInStatement = this.visitForInForOf; - - protected ForOfStatement = this.visitForInForOf; - - protected Identifier(node: TSESTree.Identifier): void { - const scope = this.getScope(node); - if ( - scope.type === TSESLint.Scope.ScopeType.function && - node.name === 'this' && - // this parameters should always be considered used as they're pseudo-parameters - 'params' in scope.block && - scope.block.params.includes(node) - ) { - this.markVariableAsUsed(node); - } - } - - protected MethodDefinition = this.visitSetter; - - protected Property = this.visitSetter; - - protected TSCallSignatureDeclaration = this.visitFunctionTypeSignature; - - protected TSConstructorType = this.visitFunctionTypeSignature; - - protected TSConstructSignatureDeclaration = this.visitFunctionTypeSignature; - - protected TSDeclareFunction = this.visitFunctionTypeSignature; - - protected TSEmptyBodyFunctionExpression = this.visitFunctionTypeSignature; - - protected TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { - // enum members create variables because they can be referenced within the enum, - // but they obviously aren't unused variables for the purposes of this rule. + private visitFunction( + node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression, + ): void { const scope = this.getScope(node); - for (const variable of scope.variables) { + // skip implicit "arguments" variable + const variable = scope.set.get('arguments'); + if (variable?.defs.length === 0) { this.markVariableAsUsed(variable); } } - protected TSFunctionType = this.visitFunctionTypeSignature; - - protected TSMappedType(node: TSESTree.TSMappedType): void { - // mapped types create a variable for their type name, but it's not necessary to reference it, - // so we shouldn't consider it as unused for the purpose of this rule. - this.markVariableAsUsed(node.key); - } - - protected TSMethodSignature = this.visitFunctionTypeSignature; - - protected TSModuleDeclaration(node: TSESTree.TSModuleDeclaration): void { - // -- global augmentation can be in any file, and they do not need exports - if (node.kind === 'global') { - this.markVariableAsUsed('global', node.parent); + private visitFunctionTypeSignature( + node: + | TSESTree.TSCallSignatureDeclaration + | TSESTree.TSConstructorType + | TSESTree.TSConstructSignatureDeclaration + | TSESTree.TSDeclareFunction + | TSESTree.TSEmptyBodyFunctionExpression + | TSESTree.TSFunctionType + | TSESTree.TSMethodSignature, + ): void { + // function type signature params create variables because they can be referenced within the signature, + // but they obviously aren't unused variables for the purposes of this rule. + for (const param of node.params) { + this.visitPattern(param, name => { + this.markVariableAsUsed(name); + }); } } - protected TSParameterProperty(node: TSESTree.TSParameterProperty): void { - let identifier: TSESTree.Identifier | null = null; - switch (node.parameter.type) { - case AST_NODE_TYPES.AssignmentPattern: - if (node.parameter.left.type === AST_NODE_TYPES.Identifier) { - identifier = node.parameter.left; - } - break; - - case AST_NODE_TYPES.Identifier: - identifier = node.parameter; - break; - } - - if (identifier) { - this.markVariableAsUsed(identifier); + private visitSetter( + node: TSESTree.MethodDefinition | TSESTree.Property, + ): void { + if (node.kind === 'set') { + // ignore setter parameters because they're syntactically required to exist + for (const param of (node.value as TSESTree.FunctionLike).params) { + this.visitPattern(param, id => { + this.markVariableAsUsed(id); + }); + } } } @@ -417,11 +418,11 @@ function isSelfReference( } const MERGABLE_TYPES = new Set([ - AST_NODE_TYPES.TSInterfaceDeclaration, - AST_NODE_TYPES.TSTypeAliasDeclaration, - AST_NODE_TYPES.TSModuleDeclaration, AST_NODE_TYPES.ClassDeclaration, AST_NODE_TYPES.FunctionDeclaration, + AST_NODE_TYPES.TSInterfaceDeclaration, + AST_NODE_TYPES.TSModuleDeclaration, + AST_NODE_TYPES.TSTypeAliasDeclaration, ]); /** * Determine if the variable is directly exported @@ -470,7 +471,7 @@ function isExported(variable: ScopeVariable): boolean { }); } -const LOGICAL_ASSIGNMENT_OPERATORS = new Set(['&&=', '||=', '??=']); +const LOGICAL_ASSIGNMENT_OPERATORS = new Set(['??=', '&&=', '||=']); /** * Determines if the variable is used. diff --git a/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts b/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts index 9558e4629efa..fd843b83a0e9 100644 --- a/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts +++ b/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, ASTUtils, @@ -199,10 +200,10 @@ function returnsConstAssertionDirectly( } interface Options { + allowDirectConstAssertionInArrowFunctions?: boolean; allowExpressions?: boolean; - allowTypedFunctionExpressions?: boolean; allowHigherOrderFunctions?: boolean; - allowDirectConstAssertionInArrowFunctions?: boolean; + allowTypedFunctionExpressions?: boolean; } /** @@ -362,6 +363,7 @@ function ancestorHasReturnType(node: FunctionNode): boolean { } export { + ancestorHasReturnType, checkFunctionExpressionReturnType, checkFunctionReturnType, doesImmediatelyReturnFunctionExpression, @@ -369,5 +371,4 @@ export { type FunctionNode, isTypedFunctionExpression, isValidFunctionExpressionReturnType, - ancestorHasReturnType, }; diff --git a/packages/eslint-plugin/src/util/getESLintCoreRule.ts b/packages/eslint-plugin/src/util/getESLintCoreRule.ts index 59b22c7292fd..97bc8620b01d 100644 --- a/packages/eslint-plugin/src/util/getESLintCoreRule.ts +++ b/packages/eslint-plugin/src/util/getESLintCoreRule.ts @@ -16,11 +16,11 @@ interface RuleMap { 'no-loop-func': typeof import('eslint/lib/rules/no-loop-func'); 'no-loss-of-precision': typeof import('eslint/lib/rules/no-loss-of-precision'); 'no-magic-numbers': typeof import('eslint/lib/rules/no-magic-numbers'); + 'no-restricted-globals': typeof import('eslint/lib/rules/no-restricted-globals'); 'no-restricted-imports': typeof import('eslint/lib/rules/no-restricted-imports'); 'no-undef': typeof import('eslint/lib/rules/no-undef'); 'no-unused-expressions': typeof import('eslint/lib/rules/no-unused-expressions'); 'no-useless-constructor': typeof import('eslint/lib/rules/no-useless-constructor'); - 'no-restricted-globals': typeof import('eslint/lib/rules/no-restricted-globals'); 'prefer-const': typeof import('eslint/lib/rules/prefer-const'); 'prefer-destructuring': typeof import('eslint/lib/rules/prefer-destructuring'); strict: typeof import('eslint/lib/rules/strict'); diff --git a/packages/eslint-plugin/src/util/getFixOrSuggest.ts b/packages/eslint-plugin/src/util/getFixOrSuggest.ts index 9e67075aa126..50ad7d2779a1 100644 --- a/packages/eslint-plugin/src/util/getFixOrSuggest.ts +++ b/packages/eslint-plugin/src/util/getFixOrSuggest.ts @@ -1,11 +1,11 @@ import type { TSESLint } from '@typescript-eslint/utils'; export function getFixOrSuggest({ - useFix, suggestion, + useFix, }: { - useFix: boolean; suggestion: TSESLint.SuggestionReportDescriptor; + useFix: boolean; }): | { fix: TSESLint.ReportFixFunction } | { suggest: TSESLint.SuggestionReportDescriptor[] } { diff --git a/packages/eslint-plugin/src/util/getForStatementHeadLoc.ts b/packages/eslint-plugin/src/util/getForStatementHeadLoc.ts index 8a7711fbe0b6..5a0a8fbc53a9 100644 --- a/packages/eslint-plugin/src/util/getForStatementHeadLoc.ts +++ b/packages/eslint-plugin/src/util/getForStatementHeadLoc.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { nullThrows } from '@typescript-eslint/utils/eslint-utils'; /** @@ -28,7 +29,7 @@ export function getForStatementHeadLoc( 'for statement must have a closing parenthesis.', ); return { - start: structuredClone(node.loc.start), end: structuredClone(closingParens.loc.end), + start: structuredClone(node.loc.start), }; } diff --git a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts index 56227d7ed11d..49ab2b742dd9 100644 --- a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts +++ b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts @@ -1,6 +1,7 @@ // adapted from https://github.com/eslint/eslint/blob/5bdaae205c3a0089ea338b382df59e21d5b06436/lib/rules/utils/ast-utils.js#L1668-L1787 import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'; import { isArrowToken, isOpeningParenToken } from './astUtils'; @@ -197,7 +198,7 @@ export function getFunctionHeadLoc( } return { - start: { ...start }, end: { ...end }, + start: { ...start }, }; } diff --git a/packages/eslint-plugin/src/util/getMemberHeadLoc.ts b/packages/eslint-plugin/src/util/getMemberHeadLoc.ts index 19401230e235..d28058d45691 100644 --- a/packages/eslint-plugin/src/util/getMemberHeadLoc.ts +++ b/packages/eslint-plugin/src/util/getMemberHeadLoc.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { nullThrows, NullThrowsReasons, @@ -30,8 +31,8 @@ export function getMemberHeadLoc( sourceCode: Readonly, node: | TSESTree.MethodDefinition - | TSESTree.TSAbstractMethodDefinition | TSESTree.PropertyDefinition + | TSESTree.TSAbstractMethodDefinition | TSESTree.TSAbstractPropertyDefinition, ): TSESTree.SourceLocation { let start: TSESTree.Position; @@ -60,8 +61,8 @@ export function getMemberHeadLoc( } return { - start: structuredClone(start), end: structuredClone(end), + start: structuredClone(start), }; } @@ -102,7 +103,7 @@ export function getParameterPropertyHeadLoc( ); return { - start, end, + start, }; } diff --git a/packages/eslint-plugin/src/util/getOperatorPrecedence.ts b/packages/eslint-plugin/src/util/getOperatorPrecedence.ts index 8aac3d6fbd2d..950708015d89 100644 --- a/packages/eslint-plugin/src/util/getOperatorPrecedence.ts +++ b/packages/eslint-plugin/src/util/getOperatorPrecedence.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/prefer-literal-enum-member -- the enums come from TS so to make merging upstream easier we purposely avoid adding literal values. */ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { SyntaxKind } from 'typescript'; @@ -320,27 +321,27 @@ export function getOperatorPrecedence( case SyntaxKind.BinaryExpression: switch (operatorKind) { - case SyntaxKind.CommaToken: - return OperatorPrecedence.Comma; - - case SyntaxKind.EqualsToken: - case SyntaxKind.PlusEqualsToken: - case SyntaxKind.MinusEqualsToken: + case SyntaxKind.AmpersandAmpersandEqualsToken: + case SyntaxKind.AmpersandEqualsToken: case SyntaxKind.AsteriskAsteriskEqualsToken: case SyntaxKind.AsteriskEqualsToken: - case SyntaxKind.SlashEqualsToken: - case SyntaxKind.PercentEqualsToken: - case SyntaxKind.LessThanLessThanEqualsToken: + case SyntaxKind.BarBarEqualsToken: + case SyntaxKind.BarEqualsToken: + case SyntaxKind.CaretEqualsToken: + case SyntaxKind.EqualsToken: case SyntaxKind.GreaterThanGreaterThanEqualsToken: case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - case SyntaxKind.AmpersandEqualsToken: - case SyntaxKind.CaretEqualsToken: - case SyntaxKind.BarEqualsToken: - case SyntaxKind.BarBarEqualsToken: - case SyntaxKind.AmpersandAmpersandEqualsToken: + case SyntaxKind.LessThanLessThanEqualsToken: + case SyntaxKind.MinusEqualsToken: + case SyntaxKind.PercentEqualsToken: + case SyntaxKind.PlusEqualsToken: case SyntaxKind.QuestionQuestionEqualsToken: + case SyntaxKind.SlashEqualsToken: return OperatorPrecedence.Assignment; + case SyntaxKind.CommaToken: + return OperatorPrecedence.Comma; + default: return getBinaryOperatorPrecedence(operatorKind); } @@ -410,81 +411,81 @@ export function getBinaryOperatorPrecedence( kind: SyntaxKind | TSESTreeOperatorKind, ): OperatorPrecedence { switch (kind) { - case SyntaxKind.QuestionQuestionToken: + case '-': + case '+': + case SyntaxKind.MinusToken: + case SyntaxKind.PlusToken: + return OperatorPrecedence.Additive; + + case '!=': + case '!==': + case '==': + case '===': + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + return OperatorPrecedence.Equality; + case '??': + case SyntaxKind.QuestionQuestionToken: return OperatorPrecedence.Coalesce; - case SyntaxKind.BarBarToken: - case '||': - return OperatorPrecedence.LogicalOR; + case '*': + case '/': + case '%': + case SyntaxKind.AsteriskToken: + case SyntaxKind.PercentToken: + case SyntaxKind.SlashToken: + return OperatorPrecedence.Multiplicative; + + case '**': + case SyntaxKind.AsteriskAsteriskToken: + return OperatorPrecedence.Exponentiation; + + case '&': + case SyntaxKind.AmpersandToken: + return OperatorPrecedence.BitwiseAND; - case SyntaxKind.AmpersandAmpersandToken: case '&&': + case SyntaxKind.AmpersandAmpersandToken: return OperatorPrecedence.LogicalAND; - case SyntaxKind.BarToken: - case '|': - return OperatorPrecedence.BitwiseOR; - - case SyntaxKind.CaretToken: case '^': + case SyntaxKind.CaretToken: return OperatorPrecedence.BitwiseXOR; - case SyntaxKind.AmpersandToken: - case '&': - return OperatorPrecedence.BitwiseAND; - - case SyntaxKind.EqualsEqualsToken: - case '==': - case SyntaxKind.ExclamationEqualsToken: - case '!=': - case SyntaxKind.EqualsEqualsEqualsToken: - case '===': - case SyntaxKind.ExclamationEqualsEqualsToken: - case '!==': - return OperatorPrecedence.Equality; - - case SyntaxKind.LessThanToken: case '<': - case SyntaxKind.GreaterThanToken: - case '>': - case SyntaxKind.LessThanEqualsToken: case '<=': - case SyntaxKind.GreaterThanEqualsToken: + case '>': case '>=': - case SyntaxKind.InstanceOfKeyword: - case 'instanceof': - case SyntaxKind.InKeyword: case 'in': + case 'instanceof': case SyntaxKind.AsKeyword: + case SyntaxKind.GreaterThanEqualsToken: + case SyntaxKind.GreaterThanToken: + case SyntaxKind.InKeyword: + case SyntaxKind.InstanceOfKeyword: + case SyntaxKind.LessThanEqualsToken: + case SyntaxKind.LessThanToken: // case 'as': -- we don't have a token for this return OperatorPrecedence.Relational; - case SyntaxKind.LessThanLessThanToken: case '<<': - case SyntaxKind.GreaterThanGreaterThanToken: case '>>': - case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: case '>>>': + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanToken: + case SyntaxKind.LessThanLessThanToken: return OperatorPrecedence.Shift; - case SyntaxKind.PlusToken: - case '+': - case SyntaxKind.MinusToken: - case '-': - return OperatorPrecedence.Additive; - - case SyntaxKind.AsteriskToken: - case '*': - case SyntaxKind.SlashToken: - case '/': - case SyntaxKind.PercentToken: - case '%': - return OperatorPrecedence.Multiplicative; + case '|': + case SyntaxKind.BarToken: + return OperatorPrecedence.BitwiseOR; - case SyntaxKind.AsteriskAsteriskToken: - case '**': - return OperatorPrecedence.Exponentiation; + case '||': + case SyntaxKind.BarBarToken: + return OperatorPrecedence.LogicalOR; } // -1 is lower than all other precedences. Returning it will cause binary expression diff --git a/packages/eslint-plugin/src/util/getStaticStringValue.ts b/packages/eslint-plugin/src/util/getStaticStringValue.ts index 6eeaf9af8ce3..103e53154ba2 100644 --- a/packages/eslint-plugin/src/util/getStaticStringValue.ts +++ b/packages/eslint-plugin/src/util/getStaticStringValue.ts @@ -1,6 +1,7 @@ // adapted from https://github.com/eslint/eslint/blob/5bdaae205c3a0089ea338b382df59e21d5b06436/lib/rules/utils/ast-utils.js#L191-L230 import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { isNullLiteral } from './isNullLiteral'; diff --git a/packages/eslint-plugin/src/util/getThisExpression.ts b/packages/eslint-plugin/src/util/getThisExpression.ts index 196254c35f96..5bcd33bdf089 100644 --- a/packages/eslint-plugin/src/util/getThisExpression.ts +++ b/packages/eslint-plugin/src/util/getThisExpression.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; export function getThisExpression( diff --git a/packages/eslint-plugin/src/util/getWrappingFixer.ts b/packages/eslint-plugin/src/util/getWrappingFixer.ts index e4d5857493d1..26afcaa6405b 100644 --- a/packages/eslint-plugin/src/util/getWrappingFixer.ts +++ b/packages/eslint-plugin/src/util/getWrappingFixer.ts @@ -1,4 +1,5 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES, ASTUtils, @@ -6,10 +7,6 @@ import { } from '@typescript-eslint/utils'; interface WrappingFixerParams { - /** Source code. */ - sourceCode: Readonly; - /** The node we want to modify. */ - node: TSESTree.Node; /** * Descendant of `node` we want to preserve. * Use this to replace some code with another. @@ -17,6 +14,10 @@ interface WrappingFixerParams { * You can pass multiple nodes as an array. */ innerNode?: TSESTree.Node | TSESTree.Node[]; + /** The node we want to modify. */ + node: TSESTree.Node; + /** Source code. */ + sourceCode: Readonly; /** * The function which gets the code of the `innerNode` and returns some code around it. * Receives multiple arguments if there are multiple innerNodes. @@ -32,7 +33,7 @@ interface WrappingFixerParams { export function getWrappingFixer( params: WrappingFixerParams, ): TSESLint.ReportFixFunction { - const { sourceCode, node, innerNode = node, wrap } = params; + const { node, innerNode = node, sourceCode, wrap } = params; const innerNodes = Array.isArray(innerNode) ? innerNode : [innerNode]; return (fixer): TSESLint.RuleFix => { @@ -83,11 +84,11 @@ export function getWrappingFixer( * @returns If parentheses are required, code for the nodeToMove node is returned with parentheses at both ends of the code. */ export function getMovedNodeCode(params: { - sourceCode: Readonly; - nodeToMove: TSESTree.Node; destinationNode: TSESTree.Node; + nodeToMove: TSESTree.Node; + sourceCode: Readonly; }): string { - const { sourceCode, nodeToMove: existingNode, destinationNode } = params; + const { destinationNode, nodeToMove: existingNode, sourceCode } = params; const code = sourceCode.getText(existingNode); if (isStrongPrecedenceNode(existingNode)) { // Moved node never needs parens diff --git a/packages/eslint-plugin/src/util/index.ts b/packages/eslint-plugin/src/util/index.ts index b13b5855231d..3f1e4b5cb2dc 100644 --- a/packages/eslint-plugin/src/util/index.ts +++ b/packages/eslint-plugin/src/util/index.ts @@ -3,6 +3,7 @@ import { ESLintUtils } from '@typescript-eslint/utils'; export * from './astUtils'; export * from './collectUnusedVariables'; export * from './createRule'; +export * from './getFixOrSuggest'; export * from './getFunctionHeadLoc'; export * from './getOperatorPrecedence'; export * from './getStaticStringValue'; @@ -10,6 +11,8 @@ export * from './getStringLength'; export * from './getTextWithParentheses'; export * from './getThisExpression'; export * from './getWrappingFixer'; +export * from './isArrayMethodCallWithPredicate'; +export * from './isAssignee'; export * from './isNodeEqual'; export * from './isNullLiteral'; export * from './isStartOfExpressionStatement'; @@ -19,17 +22,14 @@ export * from './needsPrecedingSemiColon'; export * from './objectIterators'; export * from './scopeUtils'; export * from './types'; -export * from './isAssignee'; -export * from './getFixOrSuggest'; -export * from './isArrayMethodCallWithPredicate'; // this is done for convenience - saves migrating all of the old rules export * from '@typescript-eslint/type-utils'; const { applyDefault, deepMerge, - isObjectNotArray, getParserServices, + isObjectNotArray, nullThrows, NullThrowsReasons, } = ESLintUtils; @@ -40,10 +40,10 @@ type InferOptionsTypeFromRule = ESLintUtils.InferOptionsTypeFromRule; export { applyDefault, deepMerge, - isObjectNotArray, getParserServices, - nullThrows, type InferMessageIdsTypeFromRule, type InferOptionsTypeFromRule, + isObjectNotArray, + nullThrows, NullThrowsReasons, }; diff --git a/packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts b/packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts index 39e46d9dda39..c48e1a13636d 100644 --- a/packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts +++ b/packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts @@ -1,22 +1,23 @@ -import { getConstrainedTypeAtLocation } from '@typescript-eslint/type-utils'; import type { ParserServicesWithTypeInformation, TSESTree, } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { RuleContext } from '@typescript-eslint/utils/ts-eslint'; + +import { getConstrainedTypeAtLocation } from '@typescript-eslint/type-utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import { getStaticMemberAccessValue } from './misc'; const ARRAY_PREDICATE_FUNCTIONS = new Set([ + 'every', 'filter', 'find', 'findIndex', 'findLast', 'findLastIndex', 'some', - 'every', ]); export function isArrayMethodCallWithPredicate( diff --git a/packages/eslint-plugin/src/util/isAssignee.ts b/packages/eslint-plugin/src/util/isAssignee.ts index 390243152503..9bf694827722 100644 --- a/packages/eslint-plugin/src/util/isAssignee.ts +++ b/packages/eslint-plugin/src/util/isAssignee.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; export function isAssignee(node: TSESTree.Node): boolean { diff --git a/packages/eslint-plugin/src/util/isNodeEqual.ts b/packages/eslint-plugin/src/util/isNodeEqual.ts index d783d8184285..729b38b58888 100644 --- a/packages/eslint-plugin/src/util/isNodeEqual.ts +++ b/packages/eslint-plugin/src/util/isNodeEqual.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; export function isNodeEqual(a: TSESTree.Node, b: TSESTree.Node): boolean { diff --git a/packages/eslint-plugin/src/util/isNullLiteral.ts b/packages/eslint-plugin/src/util/isNullLiteral.ts index d59a926c5aaa..a54aec1725a8 100644 --- a/packages/eslint-plugin/src/util/isNullLiteral.ts +++ b/packages/eslint-plugin/src/util/isNullLiteral.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; export function isNullLiteral(i: TSESTree.Node): i is TSESTree.NullLiteral { diff --git a/packages/eslint-plugin/src/util/isStartOfExpressionStatement.ts b/packages/eslint-plugin/src/util/isStartOfExpressionStatement.ts index 76f920dfbeb8..f5d39061c23c 100644 --- a/packages/eslint-plugin/src/util/isStartOfExpressionStatement.ts +++ b/packages/eslint-plugin/src/util/isStartOfExpressionStatement.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; // The following is copied from `eslint`'s source code. diff --git a/packages/eslint-plugin/src/util/isTypeImport.ts b/packages/eslint-plugin/src/util/isTypeImport.ts index 6e8af22d751c..b1c88d813a7d 100644 --- a/packages/eslint-plugin/src/util/isTypeImport.ts +++ b/packages/eslint-plugin/src/util/isTypeImport.ts @@ -2,6 +2,7 @@ import type { Definition, ImportBindingDefinition, } from '@typescript-eslint/scope-manager'; + import { DefinitionType } from '@typescript-eslint/scope-manager'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; diff --git a/packages/eslint-plugin/src/util/isUndefinedIdentifier.ts b/packages/eslint-plugin/src/util/isUndefinedIdentifier.ts index 75c301a1ea49..4436ead15d68 100644 --- a/packages/eslint-plugin/src/util/isUndefinedIdentifier.ts +++ b/packages/eslint-plugin/src/util/isUndefinedIdentifier.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; export function isUndefinedIdentifier(i: TSESTree.Node): boolean { diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index 41933dc551c2..4e65d2287133 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -1,10 +1,11 @@ /** * @fileoverview Really small utility functions that didn't deserve their own files */ -import { requiresQuoting } from '@typescript-eslint/type-utils'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { RuleContext } from '@typescript-eslint/utils/ts-eslint'; + +import { requiresQuoting } from '@typescript-eslint/type-utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; import { getStaticValue, isParenthesized } from './astUtils'; @@ -109,46 +110,46 @@ enum MemberNameType { */ function getNameFromMember( member: - | TSESTree.MethodDefinition | TSESTree.AccessorProperty + | TSESTree.MethodDefinition | TSESTree.Property | TSESTree.PropertyDefinition - | TSESTree.TSAbstractMethodDefinition | TSESTree.TSAbstractAccessorProperty + | TSESTree.TSAbstractMethodDefinition | TSESTree.TSAbstractPropertyDefinition | TSESTree.TSMethodSignature | TSESTree.TSPropertySignature, sourceCode: TSESLint.SourceCode, -): { type: MemberNameType; name: string } { +): { name: string; type: MemberNameType } { if (member.key.type === AST_NODE_TYPES.Identifier) { return { - type: MemberNameType.Normal, name: member.key.name, + type: MemberNameType.Normal, }; } if (member.key.type === AST_NODE_TYPES.PrivateIdentifier) { return { - type: MemberNameType.Private, name: `#${member.key.name}`, + type: MemberNameType.Private, }; } if (member.key.type === AST_NODE_TYPES.Literal) { const name = `${member.key.value}`; if (requiresQuoting(name)) { return { - type: MemberNameType.Quoted, name: `"${name}"`, + type: MemberNameType.Quoted, }; } return { - type: MemberNameType.Normal, name, + type: MemberNameType.Normal, }; } return { - type: MemberNameType.Expression, name: sourceCode.text.slice(...member.key.range), + type: MemberNameType.Expression, }; } @@ -159,7 +160,7 @@ type ExcludeKeys< type RequireKeys< Obj extends Record, Keys extends keyof Obj, -> = ExcludeKeys & { [k in Keys]-?: Exclude }; +> = { [k in Keys]-?: Exclude } & ExcludeKeys; function getEnumNames(myEnum: Record): T[] { return Object.keys(myEnum).filter(x => isNaN(Number(x))) as T[]; @@ -327,18 +328,18 @@ export { type Equal, type ExcludeKeys, findFirstResult, + findLastIndex, formatWordList, getEnumNames, - getStaticMemberAccessValue, getNameFromIndexSignature, getNameFromMember, + getStaticMemberAccessValue, isDefinitionFile, - isRestParameterDeclaration, isParenlessArrowFunction, + isRestParameterDeclaration, isStaticMemberAccessOfValue, MemberNameType, type RequireKeys, typeNodeRequiresParentheses, upperCaseFirst, - findLastIndex, }; diff --git a/packages/eslint-plugin/src/util/needsPrecedingSemiColon.ts b/packages/eslint-plugin/src/util/needsPrecedingSemiColon.ts index 5f9a94f551f4..c990a50a05a5 100644 --- a/packages/eslint-plugin/src/util/needsPrecedingSemiColon.ts +++ b/packages/eslint-plugin/src/util/needsPrecedingSemiColon.ts @@ -1,10 +1,11 @@ import type { TSESTree } from '@typescript-eslint/utils'; +import type { SourceCode } from '@typescript-eslint/utils/ts-eslint'; + import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { isClosingBraceToken, isClosingParenToken, } from '@typescript-eslint/utils/ast-utils'; -import type { SourceCode } from '@typescript-eslint/utils/ts-eslint'; // The following is adapted from `eslint`'s source code. // https://github.com/eslint/eslint/blob/3a4eaf921543b1cd5d1df4ea9dec02fab396af2a/lib/rules/utils/ast-utils.js#L1043-L1132 @@ -43,7 +44,7 @@ const NODE_TYPES_BY_KEYWORD: Record = { * Before an opening parenthesis, postfix `++` and `--` always trigger ASI; * the tokens `:`, `;`, `{` and `=>` don't expect a semicolon, as that would count as an empty statement. */ -const PUNCTUATORS = new Set([':', ';', '{', '=>', '++', '--']); +const PUNCTUATORS = new Set(['--', ';', ':', '{', '++', '=>']); /* * Statements that can contain an `ExpressionStatement` after a closing parenthesis. diff --git a/packages/eslint-plugin/src/util/rangeToLoc.ts b/packages/eslint-plugin/src/util/rangeToLoc.ts index 893362d3a722..5a969d2cb265 100644 --- a/packages/eslint-plugin/src/util/rangeToLoc.ts +++ b/packages/eslint-plugin/src/util/rangeToLoc.ts @@ -5,7 +5,7 @@ export function rangeToLoc( range: TSESLint.AST.Range, ): TSESTree.SourceLocation { return { - start: sourceCode.getLocFromIndex(range[0]), end: sourceCode.getLocFromIndex(range[1]), + start: sourceCode.getLocFromIndex(range[0]), }; } diff --git a/packages/eslint-plugin/src/util/referenceContainsTypeQuery.ts b/packages/eslint-plugin/src/util/referenceContainsTypeQuery.ts index 60872beeddb5..2d5a42a102e2 100644 --- a/packages/eslint-plugin/src/util/referenceContainsTypeQuery.ts +++ b/packages/eslint-plugin/src/util/referenceContainsTypeQuery.ts @@ -1,4 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; + import { AST_NODE_TYPES } from '@typescript-eslint/utils'; /** diff --git a/packages/eslint-plugin/src/util/types.ts b/packages/eslint-plugin/src/util/types.ts index 84991966a237..0765b2683d6e 100644 --- a/packages/eslint-plugin/src/util/types.ts +++ b/packages/eslint-plugin/src/util/types.ts @@ -1,5 +1,5 @@ -export type MakeRequired = Omit & { +export type MakeRequired = { [K in Key]-?: NonNullable; -}; +} & Omit; export type ValueOf = T[keyof T]; diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts index 12511185f9ae..bc0317c8a466 100644 --- a/packages/eslint-plugin/tests/RuleTester.ts +++ b/packages/eslint-plugin/tests/RuleTester.ts @@ -1,10 +1,10 @@ -import * as path from 'node:path'; - import type { InvalidTestCase, ValidTestCase, } from '@typescript-eslint/rule-tester'; +import * as path from 'node:path'; + export function getFixturesRootDir(): string { return path.join(__dirname, 'fixtures'); } @@ -46,9 +46,9 @@ export function batchedSingleLineTests< Options extends readonly unknown[], >( options: - | (Omit, 'output'> & { + | ({ output?: string | null; - }) + } & Omit, 'output'>) | ValidTestCase, ): (InvalidTestCase | ValidTestCase)[] { // -- eslint counts lines from 1 diff --git a/packages/eslint-plugin/tests/areOptionsValid.test.ts b/packages/eslint-plugin/tests/areOptionsValid.test.ts index 1908acf1460f..dd5ddfabc12d 100644 --- a/packages/eslint-plugin/tests/areOptionsValid.test.ts +++ b/packages/eslint-plugin/tests/areOptionsValid.test.ts @@ -2,19 +2,19 @@ import { createRule } from '../src/util'; import { areOptionsValid } from './areOptionsValid'; const exampleRule = createRule<['value-a' | 'value-b'], never>({ - name: 'my-example-rule', + create() { + return {}; + }, + defaultOptions: ['value-a'], meta: { - type: 'suggestion', docs: { description: 'Detects something or other', }, - schema: [{ type: 'string', enum: ['value-a', 'value-b'] }], messages: {}, + schema: [{ enum: ['value-a', 'value-b'], type: 'string' }], + type: 'suggestion', }, - defaultOptions: ['value-a'], - create() { - return {}; - }, + name: 'my-example-rule', }); test('returns true for valid options', () => { diff --git a/packages/eslint-plugin/tests/areOptionsValid.ts b/packages/eslint-plugin/tests/areOptionsValid.ts index 807653deb2ff..177d000e0ac8 100644 --- a/packages/eslint-plugin/tests/areOptionsValid.ts +++ b/packages/eslint-plugin/tests/areOptionsValid.ts @@ -1,8 +1,9 @@ -import { TSUtils } from '@typescript-eslint/utils'; import type { RuleModule } from '@typescript-eslint/utils/ts-eslint'; -import Ajv from 'ajv'; import type { JSONSchema4 } from 'json-schema'; +import { TSUtils } from '@typescript-eslint/utils'; +import Ajv from 'ajv'; + const ajv = new Ajv({ async: false }); export function areOptionsValid( @@ -29,16 +30,16 @@ function normalizeSchema( if (schema.length === 0) { return { - type: 'array', - minItems: 0, maxItems: 0, + minItems: 0, + type: 'array', }; } return { - type: 'array', items: schema as JSONSchema4[], - minItems: 0, maxItems: schema.length, + minItems: 0, + type: 'array', }; } diff --git a/packages/eslint-plugin/tests/configs.test.ts b/packages/eslint-plugin/tests/configs.test.ts index 7aa30ab5ddff..3c62311271d3 100644 --- a/packages/eslint-plugin/tests/configs.test.ts +++ b/packages/eslint-plugin/tests/configs.test.ts @@ -17,8 +17,8 @@ const EXTENSION_RULES = Object.entries(rules) ); function filterRules( - values: Record, -): [string, string | unknown[]][] { + values: Record, +): [string, unknown[] | string][] { return Object.entries(values).filter(([name]) => name.startsWith(RULE_NAME_PREFIX), ); @@ -26,14 +26,14 @@ function filterRules( interface FilterAndMapRuleConfigsSettings { excludeDeprecated?: boolean; - typeChecked?: 'exclude' | 'include-only'; recommendations?: (RuleRecommendation | undefined)[]; + typeChecked?: 'exclude' | 'include-only'; } function filterAndMapRuleConfigs({ excludeDeprecated, - typeChecked, recommendations, + typeChecked, }: FilterAndMapRuleConfigsSettings = {}): [string, unknown][] { let result = Object.entries(rules); @@ -84,7 +84,7 @@ function filterAndMapRuleConfigs({ } function itHasBaseRulesOverriden( - unfilteredConfigRules: Record, + unfilteredConfigRules: Record, ): void { it('has the base rules overriden by the appropriate extension rules', () => { const ruleNames = new Set(Object.keys(unfilteredConfigRules)); @@ -144,8 +144,8 @@ describe('recommended.ts', () => { const configRules = filterRules(unfilteredConfigRules); // note: include deprecated rules so that the config doesn't change between major bumps const ruleConfigs = filterAndMapRuleConfigs({ - typeChecked: 'exclude', recommendations: ['recommended'], + typeChecked: 'exclude', }); expect(Object.fromEntries(ruleConfigs)).toEqual( @@ -183,8 +183,8 @@ describe('recommended-type-checked-only.ts', () => { const configRules = filterRules(unfilteredConfigRules); // note: include deprecated rules so that the config doesn't change between major bumps const ruleConfigs = filterAndMapRuleConfigs({ - typeChecked: 'include-only', recommendations: ['recommended'], + typeChecked: 'include-only', }).filter(([ruleName]) => ruleName); expect(Object.fromEntries(ruleConfigs)).toEqual( @@ -196,7 +196,7 @@ describe('recommended-type-checked-only.ts', () => { }); describe('strict.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs.strict.rules; it('contains all strict rules, excluding type checked ones', () => { @@ -204,8 +204,8 @@ describe('strict.ts', () => { // note: exclude deprecated rules, this config is allowed to change between minor versions const ruleConfigs = filterAndMapRuleConfigs({ excludeDeprecated: true, - typeChecked: 'exclude', recommendations: ['recommended', 'strict'], + typeChecked: 'exclude', }); expect(Object.fromEntries(ruleConfigs)).toEqual( @@ -217,7 +217,7 @@ describe('strict.ts', () => { }); describe('strict-type-checked.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs['strict-type-checked'].rules; it('contains all strict rules', () => { @@ -236,7 +236,7 @@ describe('strict-type-checked.ts', () => { }); describe('strict-type-checked-only.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs['strict-type-checked-only'].rules; it('contains only type-checked strict rules', () => { @@ -244,8 +244,8 @@ describe('strict-type-checked-only.ts', () => { // note: exclude deprecated rules, this config is allowed to change between minor versions const ruleConfigs = filterAndMapRuleConfigs({ excludeDeprecated: true, - typeChecked: 'include-only', recommendations: ['recommended', 'strict'], + typeChecked: 'include-only', }).filter(([ruleName]) => ruleName); expect(Object.fromEntries(ruleConfigs)).toEqual( @@ -257,15 +257,15 @@ describe('strict-type-checked-only.ts', () => { }); describe('stylistic.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs.stylistic.rules; it('contains all stylistic rules, excluding deprecated or type checked ones', () => { const configRules = filterRules(unfilteredConfigRules); // note: include deprecated rules so that the config doesn't change between major bumps const ruleConfigs = filterAndMapRuleConfigs({ - typeChecked: 'exclude', recommendations: ['stylistic'], + typeChecked: 'exclude', }); expect(Object.fromEntries(ruleConfigs)).toEqual( @@ -302,8 +302,8 @@ describe('stylistic-type-checked-only.ts', () => { const configRules = filterRules(unfilteredConfigRules); // note: include deprecated rules so that the config doesn't change between major bumps const ruleConfigs = filterAndMapRuleConfigs({ - typeChecked: 'include-only', recommendations: ['stylistic'], + typeChecked: 'include-only', }).filter(([ruleName]) => ruleName); expect(Object.fromEntries(ruleConfigs)).toEqual( diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-literal-enum-member.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-literal-enum-member.shot index 687ca0e7747a..80e574d2d03f 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-literal-enum-member.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-literal-enum-member.shot @@ -39,19 +39,19 @@ Options: { "allowBitwiseExpressions": true } const x = 1; enum Foo { A = x << 0, - ~ Explicit enum value must only be a literal value (string or number). + ~ Explicit enum value must only be a literal value (string or number) or a bitwise expression. B = x >> 0, - ~ Explicit enum value must only be a literal value (string or number). + ~ Explicit enum value must only be a literal value (string or number) or a bitwise expression. C = x >>> 0, - ~ Explicit enum value must only be a literal value (string or number). + ~ Explicit enum value must only be a literal value (string or number) or a bitwise expression. D = x | 0, - ~ Explicit enum value must only be a literal value (string or number). + ~ Explicit enum value must only be a literal value (string or number) or a bitwise expression. E = x & 0, - ~ Explicit enum value must only be a literal value (string or number). + ~ Explicit enum value must only be a literal value (string or number) or a bitwise expression. F = x ^ 0, - ~ Explicit enum value must only be a literal value (string or number). + ~ Explicit enum value must only be a literal value (string or number) or a bitwise expression. G = ~x, - ~ Explicit enum value must only be a literal value (string or number). + ~ Explicit enum value must only be a literal value (string or number) or a bitwise expression. } " `; diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index fd8405d62833..c94c88e9b931 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -1,19 +1,18 @@ -import 'jest-specific-snapshot'; - -import assert from 'node:assert/strict'; -import fs from 'node:fs'; -import path from 'node:path'; +import type * as mdast from 'mdast'; +import type { fromMarkdown as FromMarkdown } from 'mdast-util-from-markdown' with { 'resolution-mode': 'import' }; +import type { mdxFromMarkdown as MdxFromMarkdown } from 'mdast-util-mdx' with { 'resolution-mode': 'import' }; +import type { mdxjs as Mdxjs } from 'micromark-extension-mdxjs' with { 'resolution-mode': 'import' }; +import type * as UnistUtilVisit from 'unist-util-visit' with { 'resolution-mode': 'import' }; import { parseForESLint } from '@typescript-eslint/parser'; import * as tseslintParser from '@typescript-eslint/parser'; import { Linter } from '@typescript-eslint/utils/ts-eslint'; +import 'jest-specific-snapshot'; import { marked } from 'marked'; -import type * as mdast from 'mdast'; -import type { fromMarkdown as FromMarkdown } from 'mdast-util-from-markdown' with { 'resolution-mode': 'import' }; -import type { mdxFromMarkdown as MdxFromMarkdown } from 'mdast-util-mdx' with { 'resolution-mode': 'import' }; -import type { mdxjs as Mdxjs } from 'micromark-extension-mdxjs' with { 'resolution-mode': 'import' }; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; import { titleCase } from 'title-case'; -import type * as UnistUtilVisit from 'unist-util-visit' with { 'resolution-mode': 'import' }; import rules from '../src/rules'; import { areOptionsValid } from './areOptionsValid'; @@ -43,7 +42,7 @@ type TokenType = marked.Token['type']; function tokenIs( token: marked.Token, type: Type, -): token is marked.Token & { type: Type } { +): token is { type: Type } & marked.Token { return token.type === type; } @@ -53,7 +52,7 @@ function tokenIsHeading(token: marked.Token): token is marked.Tokens.Heading { function tokenIsH2( token: marked.Token, -): token is marked.Tokens.Heading & { depth: 2 } { +): token is { depth: 2 } & marked.Tokens.Heading { return ( tokenIsHeading(token) && token.depth === 2 && !/[a-z]+: /.test(token.text) ); @@ -152,8 +151,8 @@ describe('Validating rule docs', () => { const ignoredFiles = new Set([ 'README.md', - 'TEMPLATE.md', 'shared', + 'TEMPLATE.md', // These rule docs were left behind on purpose for legacy reasons. See the // comments in the files for more information. 'ban-types.md', @@ -229,10 +228,10 @@ describe('Validating rule docs', () => { type: 'hr', }); expect(tokens[1]).toMatchObject({ + depth: 2, text: description.includes("'") ? `description: "${description}."` : `description: '${description}.'`, - depth: 2, type: 'heading', }); }); @@ -263,11 +262,11 @@ describe('Validating rule docs', () => { const requiredHeadings = ['When Not To Use It']; const importantHeadings = new Set([ - ...requiredHeadings, 'How to Use', 'Options', 'Related To', 'When Not To Use It', + ...requiredHeadings, ]); test('important headings must be h2s', () => { @@ -377,8 +376,8 @@ describe('Validating rule docs', () => { jsx: /^tsx\b/i.test(lang), }, ecmaVersion: 'latest', - sourceType: 'module', range: true, + sourceType: 'module', }); } catch { throw new Error(`Parsing error:\n\n${token.text}`); @@ -436,7 +435,7 @@ describe('Validating rule docs', () => { function lintCodeBlock( token: mdast.Code, - shouldContainLintErrors: boolean | 'skip-check', + shouldContainLintErrors: 'skip-check' | boolean, ): void { const lang = token.lang?.trim(); if (!lang || !/^tsx?\b/i.test(lang)) { @@ -468,8 +467,8 @@ describe('Validating rule docs', () => { parser: '@typescript-eslint/parser', parserOptions: { disallowAutomaticSingleRunInference: true, - tsconfigRootDir: rootPath, project: './tsconfig.json', + tsconfigRootDir: rootPath, }, rules: { [ruleName]: ruleConfig, diff --git a/packages/eslint-plugin/tests/eslint-rules/arrow-parens.test.ts b/packages/eslint-plugin/tests/eslint-rules/arrow-parens.test.ts index 9f27b18b35ec..2abf2dbb843b 100644 --- a/packages/eslint-plugin/tests/eslint-rules/arrow-parens.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/arrow-parens.test.ts @@ -7,6 +7,7 @@ const rule = getESLintCoreRule('arrow-parens'); const ruleTester = new RuleTester(); ruleTester.run('arrow-parens', rule, { + invalid: [], valid: [ // https://github.com/typescript-eslint/typescript-eslint/issues/14 noFormat`const foo = (t) => {};`, @@ -40,5 +41,4 @@ const foo = (bar: any): void => { options: ['as-needed', { requireForBlockBody: true }], }, ], - invalid: [], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/no-dupe-args.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-dupe-args.test.ts index 0050a131761b..6b51c17c048c 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-dupe-args.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-dupe-args.test.ts @@ -7,6 +7,7 @@ const rule = getESLintCoreRule('no-dupe-args'); const ruleTester = new RuleTester(); ruleTester.run('no-dupe-args', rule, { + invalid: [], valid: [ // https://github.com/eslint/typescript-eslint-parser/issues/535 ` @@ -15,5 +16,4 @@ function foo({ bar }: { bar: string }) { } `, ], - invalid: [], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/no-implicit-globals.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-implicit-globals.test.ts index ebac70b7d2f7..197c215230fb 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-implicit-globals.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-implicit-globals.test.ts @@ -6,6 +6,7 @@ const rule = getESLintCoreRule('no-implicit-globals'); const ruleTester = new RuleTester(); ruleTester.run('no-implicit-globals', rule, { + invalid: [], valid: [ // https://github.com/typescript-eslint/typescript-eslint/issues/23 ` @@ -16,5 +17,4 @@ function foo() { module.exports = foo; `, ], - invalid: [], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts index 2958d9d13cfd..ac03e65f49b9 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts @@ -7,46 +7,6 @@ const rule = getESLintCoreRule('no-restricted-globals'); const ruleTester = new RuleTester(); ruleTester.run('no-restricted-globals', rule, { - valid: [ - // https://github.com/eslint/typescript-eslint-parser/issues/487 - { - code: ` -export default class Test { - private status: string; - getStatus() { - return this.status; - } -} - `, - options: ['status'], - }, - { - code: ` -type Handler = (event: string) => any; - `, - options: ['event'], - }, - { - code: ` - const a = foo?.bar?.name; - `, - }, - { - code: ` - const a = foo?.bar?.name ?? 'foobar'; - `, - }, - { - code: ` - const a = foo()?.bar; - `, - }, - { - code: ` - const a = foo()?.bar ?? true; - `, - }, - ], invalid: [ { code: ` @@ -56,43 +16,83 @@ function onClick() { fdescribe('foo', function () {}); `, - options: ['event'], errors: [ { - messageId: 'defaultMessage', data: { name: 'event', }, + messageId: 'defaultMessage', }, ], + options: ['event'], }, { code: ` confirm('TEST'); `, - options: ['confirm'], errors: [ { - messageId: 'defaultMessage', data: { name: 'confirm', }, + messageId: 'defaultMessage', }, ], + options: ['confirm'], }, { code: ` var a = confirm('TEST')?.a; `, - options: ['confirm'], errors: [ { - messageId: 'defaultMessage', data: { name: 'confirm', }, + messageId: 'defaultMessage', }, ], + options: ['confirm'], + }, + ], + valid: [ + // https://github.com/eslint/typescript-eslint-parser/issues/487 + { + code: ` +export default class Test { + private status: string; + getStatus() { + return this.status; + } +} + `, + options: ['status'], + }, + { + code: ` +type Handler = (event: string) => any; + `, + options: ['event'], + }, + { + code: ` + const a = foo?.bar?.name; + `, + }, + { + code: ` + const a = foo?.bar?.name ?? 'foobar'; + `, + }, + { + code: ` + const a = foo()?.bar; + `, + }, + { + code: ` + const a = foo()?.bar ?? true; + `, }, ], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts index 2180223d1eab..3668c6c98135 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts @@ -7,6 +7,183 @@ const rule = getESLintCoreRule('no-undef'); const ruleTester = new RuleTester(); ruleTester.run('no-undef', rule, { + invalid: [ + { + code: 'a = 5;', + errors: [ + { + data: { + name: 'a', + }, + messageId: 'undef', + }, + ], + }, + { + code: 'a?.b = 5;', + errors: [ + { + data: { + name: 'a', + }, + messageId: 'undef', + }, + ], + }, + { + code: 'a()?.b = 5;', + errors: [ + { + data: { + name: 'a', + }, + messageId: 'undef', + }, + ], + }, + { + code: ';', + errors: [ + { + column: 2, + data: { + name: 'Foo', + }, + line: 1, + messageId: 'undef', + }, + ], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + { + code: ` +function Foo() {} +; + `, + errors: [ + { + column: 12, + data: { + name: 'x', + }, + line: 3, + messageId: 'undef', + }, + ], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + { + code: ` +function Foo() {} +; + `, + errors: [ + { + column: 10, + data: { + name: 'x', + }, + line: 3, + messageId: 'undef', + }, + ], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + { + code: ` +function Foo() {} + />; + `, + errors: [ + { + column: 6, + data: { + name: 'T', + }, + line: 3, + messageId: 'undef', + }, + ], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + { + code: ` +function Foo() {} +{x}; + `, + errors: [ + { + column: 7, + data: { + name: 'x', + }, + line: 3, + messageId: 'undef', + }, + ], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + { + code: ` +class Foo { + [x: Bar]: string; +} + `, + errors: [ + { + data: { + name: 'Bar', + }, + messageId: 'undef', + }, + ], + }, + { + code: ` +class Foo { + [x: string]: Bar; +} + `, + errors: [ + { + data: { + name: 'Bar', + }, + messageId: 'undef', + }, + ], + }, + ], valid: [ ` import Beemo from './Beemo'; @@ -274,181 +451,4 @@ class Foo { } `, ], - invalid: [ - { - code: 'a = 5;', - errors: [ - { - messageId: 'undef', - data: { - name: 'a', - }, - }, - ], - }, - { - code: 'a?.b = 5;', - errors: [ - { - messageId: 'undef', - data: { - name: 'a', - }, - }, - ], - }, - { - code: 'a()?.b = 5;', - errors: [ - { - messageId: 'undef', - data: { - name: 'a', - }, - }, - ], - }, - { - code: ';', - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - errors: [ - { - messageId: 'undef', - data: { - name: 'Foo', - }, - line: 1, - column: 2, - }, - ], - }, - { - code: ` -function Foo() {} -; - `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - errors: [ - { - messageId: 'undef', - data: { - name: 'x', - }, - line: 3, - column: 12, - }, - ], - }, - { - code: ` -function Foo() {} -; - `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - errors: [ - { - messageId: 'undef', - data: { - name: 'x', - }, - line: 3, - column: 10, - }, - ], - }, - { - code: ` -function Foo() {} - />; - `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - errors: [ - { - messageId: 'undef', - data: { - name: 'T', - }, - line: 3, - column: 6, - }, - ], - }, - { - code: ` -function Foo() {} -{x}; - `, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - errors: [ - { - messageId: 'undef', - data: { - name: 'x', - }, - line: 3, - column: 7, - }, - ], - }, - { - code: ` -class Foo { - [x: Bar]: string; -} - `, - errors: [ - { - messageId: 'undef', - data: { - name: 'Bar', - }, - }, - ], - }, - { - code: ` -class Foo { - [x: string]: Bar; -} - `, - errors: [ - { - messageId: 'undef', - data: { - name: 'Bar', - }, - }, - ], - }, - ], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts b/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts index 5223be374c4a..9bd707aedf20 100644 --- a/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/prefer-const.test.ts @@ -7,6 +7,7 @@ const rule = getESLintCoreRule('prefer-const'); const ruleTester = new RuleTester(); ruleTester.run('prefer-const', rule, { + invalid: [], valid: [ ` let x: number | undefined = 1; @@ -21,5 +22,4 @@ let x: number | undefined = 1; (x as number) += 1; `, ], - invalid: [], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/strict.test.ts b/packages/eslint-plugin/tests/eslint-rules/strict.test.ts index e25d6f7a44da..41bf41cb129f 100644 --- a/packages/eslint-plugin/tests/eslint-rules/strict.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/strict.test.ts @@ -7,16 +7,6 @@ const rule = getESLintCoreRule('strict'); const ruleTester = new RuleTester(); ruleTester.run('strict', rule, { - valid: [ - // https://github.com/typescript-eslint/typescript-eslint/issues/58 - ` -window.whatevs = { - myFunc() { - console.log('yep'); - }, -}; - `, - ], invalid: [ { // https://github.com/typescript-eslint/typescript-eslint/issues/58 @@ -27,20 +17,30 @@ window.whatevs = { }, }; `, - languageOptions: { - parserOptions: { - sourceType: 'script', - }, - }, errors: [ { - message: "Use the function form of 'use strict'.", - line: 3, column: 9, + line: 3, + message: "Use the function form of 'use strict'.", // the base rule doesn't use messageId // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any, ], + languageOptions: { + parserOptions: { + sourceType: 'script', + }, + }, }, ], + valid: [ + // https://github.com/typescript-eslint/typescript-eslint/issues/58 + ` +window.whatevs = { + myFunc() { + console.log('yep'); + }, +}; + `, + ], }); diff --git a/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts b/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts index 3839469351ce..a6469f6ffe64 100644 --- a/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts +++ b/packages/eslint-plugin/tests/rules/adjacent-overload-signatures.test.ts @@ -279,10 +279,10 @@ function wrap() { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 6, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -298,10 +298,10 @@ if (true) { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 6, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -315,10 +315,10 @@ export function foo(sn: string | number) {} `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 6, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -332,10 +332,10 @@ export function foo(sn: string | number) {} `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 6, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -349,10 +349,10 @@ function foo(sn: string | number) {} `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 6, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -366,10 +366,10 @@ function foo(sn: string | number) {} `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 6, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -383,10 +383,10 @@ function foo(sn: string | number) {} `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 6, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -399,10 +399,10 @@ function foo(sn: string | number) {} `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 5, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -420,10 +420,10 @@ class Bar { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 9, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -437,10 +437,10 @@ declare function foo(sn: string | number); `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 6, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -454,10 +454,10 @@ declare function foo(sn: string | number); `, errors: [ { - messageId: 'adjacentSignature', + column: 1, data: { name: 'foo' }, line: 6, - column: 1, + messageId: 'adjacentSignature', }, ], }, @@ -473,10 +473,10 @@ declare module 'Foo' { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -494,10 +494,10 @@ declare module 'Foo' { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'baz' }, line: 8, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -513,10 +513,10 @@ declare namespace Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -534,10 +534,10 @@ declare namespace Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'baz' }, line: 8, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -553,10 +553,10 @@ type Foo = { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -572,10 +572,10 @@ type Foo = { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -592,10 +592,10 @@ type Foo = { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -613,10 +613,10 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'call' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -632,10 +632,10 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -651,10 +651,10 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -670,10 +670,10 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -690,10 +690,10 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -711,10 +711,10 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 5, data: { name: 'baz' }, line: 8, - column: 5, + messageId: 'adjacentSignature', }, ], }, @@ -730,10 +730,10 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'new' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -749,16 +749,16 @@ interface Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'new' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, { - messageId: 'adjacentSignature', + column: 3, data: { name: 'new' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -774,10 +774,10 @@ class Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'constructor' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -793,10 +793,10 @@ class Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -812,10 +812,10 @@ class Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 7, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -832,10 +832,10 @@ class Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 8, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -852,10 +852,10 @@ class Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'constructor' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -872,10 +872,10 @@ class Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'foo' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -892,10 +892,10 @@ class Foo { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: 'static foo' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, ], }, @@ -911,16 +911,16 @@ class Test { `, errors: [ { - messageId: 'adjacentSignature', + column: 3, data: { name: '#private' }, line: 5, - column: 3, + messageId: 'adjacentSignature', }, { - messageId: 'adjacentSignature', + column: 3, data: { name: '"#private"' }, line: 6, - column: 3, + messageId: 'adjacentSignature', }, ], }, diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index 52ce7be67578..ed58c32d1fe2 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -3,6 +3,7 @@ import { RuleTester } from '@typescript-eslint/rule-tester'; import { TSESLint } from '@typescript-eslint/utils'; import type { OptionString } from '../../src/rules/array-type'; + import rule from '../../src/rules/array-type'; import { areOptionsValid } from '../areOptionsValid'; @@ -408,958 +409,958 @@ function bazFunction(baz: Arr>) { // Base cases from https://github.com/typescript-eslint/typescript-eslint/issues/2323#issuecomment-663977655 { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let a: number[] = [];', }, { code: 'let a: Array = [];', - output: 'let a: (string | number)[] = [];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let a: (string | number)[] = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly (string | number)[] = [];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let a: readonly (string | number)[] = [];', }, { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'array' }], + output: 'let a: number[] = [];', }, { code: 'let a: Array = [];', - output: 'let a: (string | number)[] = [];', - options: [{ default: 'array', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'array' }], + output: 'let a: (string | number)[] = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'array', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'array' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly (string | number)[] = [];', - options: [{ default: 'array', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'array' }], + output: 'let a: readonly (string | number)[] = [];', }, { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'array-simple' }], + output: 'let a: number[] = [];', }, { code: 'let a: Array = [];', - output: 'let a: (string | number)[] = [];', - options: [{ default: 'array', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'array-simple' }], + output: 'let a: (string | number)[] = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'array', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array', readonly: 'array-simple' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'array', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array', readonly: 'array-simple' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array', readonly: 'generic' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'generic' }], + output: 'let a: number[] = [];', }, { code: 'let a: Array = [];', - output: 'let a: (string | number)[] = [];', - options: [{ default: 'array', readonly: 'generic' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array', readonly: 'generic' }], + output: 'let a: (string | number)[] = [];', }, { code: 'let a: readonly number[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'array', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'array', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'array', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'array', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let a: number[] = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let a: Array = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array-simple', readonly: 'array' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple', readonly: 'array' }], + output: 'let a: number[] = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'array-simple', readonly: 'array' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple', readonly: 'array' }], + output: 'let a: Array = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'array-simple', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array-simple', readonly: 'array' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly (string | number)[] = [];', - options: [{ default: 'array-simple', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array-simple', readonly: 'array' }], + output: 'let a: readonly (string | number)[] = [];', }, { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array-simple', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple', readonly: 'array-simple' }], + output: 'let a: number[] = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'array-simple', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple', readonly: 'array-simple' }], + output: 'let a: Array = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'array-simple', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple', readonly: 'array-simple' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'array-simple', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple', readonly: 'array-simple' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: Array = [];', - output: 'let a: number[] = [];', - options: [{ default: 'array-simple', readonly: 'generic' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple', readonly: 'generic' }], + output: 'let a: number[] = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'array-simple', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple', readonly: 'generic' }], + output: 'let a: Array = [];', }, { code: 'let a: readonly number[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'array-simple', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'array-simple', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'array-simple', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], - }, + options: [{ default: 'array-simple', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', + }, { code: 'let a: number[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let a: Array = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let a: Array = [];', }, { code: 'let a: readonly number[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: number[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'array' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'array' }], + output: 'let a: Array = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'array' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'array' }], + output: 'let a: Array = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'generic', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'generic', readonly: 'array' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly (string | number)[] = [];', - options: [{ default: 'generic', readonly: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'generic', readonly: 'array' }], + output: 'let a: readonly (string | number)[] = [];', }, { code: 'let a: number[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'array-simple' }], + output: 'let a: Array = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'array-simple' }], + output: 'let a: Array = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly number[] = [];', - options: [{ default: 'generic', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'generic', readonly: 'array-simple' }], + output: 'let a: readonly number[] = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'generic', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'generic', readonly: 'array-simple' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: number[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'generic' }], + output: 'let a: Array = [];', }, { code: 'let a: (string | number)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'generic' }], + output: 'let a: Array = [];', }, { code: 'let a: readonly number[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'generic', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'number', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: readonly (string | number)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'generic', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: bigint[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'bigint' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'array-simple' }], + output: 'let a: Array = [];', }, { code: 'let a: (string | bigint)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'array-simple' }], + output: 'let a: Array = [];', }, { code: 'let a: ReadonlyArray = [];', - output: 'let a: readonly bigint[] = [];', - options: [{ default: 'generic', readonly: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'bigint', }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'generic', readonly: 'array-simple' }], + output: 'let a: readonly bigint[] = [];', }, { code: 'let a: (string | bigint)[] = [];', - output: 'let a: Array = [];', - options: [{ default: 'generic', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'generic' }], + output: 'let a: Array = [];', }, { code: 'let a: readonly bigint[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'generic', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'bigint', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, { code: 'let a: readonly (string | bigint)[] = [];', - output: 'let a: ReadonlyArray = [];', - options: [{ default: 'generic', readonly: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic', readonly: 'generic' }], + output: 'let a: ReadonlyArray = [];', }, // End of base cases { code: 'let a: { foo: Array }[] = [];', - output: 'let a: { foo: Bar[] }[] = [];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 15, data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, - column: 15, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let a: { foo: Bar[] }[] = [];', }, { code: 'let a: Array<{ foo: Bar[] }> = [];', - output: 'let a: Array<{ foo: Array }> = [];', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 21, data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, - column: 21, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let a: Array<{ foo: Array }> = [];', }, { code: 'let a: Array<{ foo: Foo | Bar[] }> = [];', - output: 'let a: Array<{ foo: Foo | Array }> = [];', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 27, data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, - column: 27, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let a: Array<{ foo: Foo | Array }> = [];', }, { code: 'function foo(a: Array): Array {}', - output: 'function foo(a: Bar[]): Bar[] {}', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 17, data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, - column: 17, + messageId: 'errorStringArray', }, { - messageId: 'errorStringArray', + column: 30, data: { className: 'Array', readonlyPrefix: '', type: 'Bar' }, line: 1, - column: 30, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'function foo(a: Bar[]): Bar[] {}', }, { code: 'let x: Array = [undefined] as undefined[];', - output: 'let x: undefined[] = [undefined] as undefined[];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'undefined' }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let x: undefined[] = [undefined] as undefined[];', }, { code: "let y: string[] = >['2'];", - output: "let y: string[] = ['2'];", - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 20, data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 1, - column: 20, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: "let y: string[] = ['2'];", }, { code: "let z: Array = [3, '4'];", - output: "let z: any[] = [3, '4'];", - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: "let z: any[] = [3, '4'];", }, { code: "let ya = [[1, '2']] as [number, string][];", - output: "let ya = [[1, '2']] as Array<[number, string]>;", - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 24, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 24, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: "let ya = [[1, '2']] as Array<[number, string]>;", }, { code: 'type Arr = Array;', - output: 'type Arr = T[];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 15, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 15, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: 'type Arr = T[];', }, { code: ` // Ignore user defined aliases let yyyy: Arr>[]> = [[[['2']]]]; `, - output: ` -// Ignore user defined aliases -let yyyy: Arr>>> = [[[['2']]]]; - `, - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 15, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, - column: 15, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: ` +// Ignore user defined aliases +let yyyy: Arr>>> = [[[['2']]]]; + `, }, { code: ` @@ -1370,6 +1371,15 @@ interface ArrayClass { xyz: this[]; } `, + errors: [ + { + column: 8, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, + line: 3, + messageId: 'errorStringArraySimple', + }, + ], + options: [{ default: 'array-simple' }], output: ` interface ArrayClass { foo: T[]; @@ -1378,15 +1388,6 @@ interface ArrayClass { xyz: this[]; } `, - options: [{ default: 'array-simple' }], - errors: [ - { - messageId: 'errorStringArraySimple', - data: { className: 'Array', readonlyPrefix: '', type: 'T' }, - line: 3, - column: 8, - }, - ], }, { code: ` @@ -1394,160 +1395,160 @@ function barFunction(bar: ArrayClass[]) { return bar.map(e => e.bar); } `, - output: ` -function barFunction(bar: Array>) { - return bar.map(e => e.bar); -} - `, - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 27, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 2, - column: 27, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: ` +function barFunction(bar: Array>) { + return bar.map(e => e.bar); +} + `, }, { code: 'let barVar: ((c: number) => number)[];', - output: 'let barVar: Array<(c: number) => number>;', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 13, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 13, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let barVar: Array<(c: number) => number>;', }, { code: 'type barUnion = (string | number | boolean)[];', - output: 'type barUnion = Array;', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 17, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 17, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: 'type barUnion = Array;', }, { code: 'type barIntersection = (string & number)[];', - output: 'type barIntersection = Array;', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 24, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 24, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: 'type barIntersection = Array;', }, { code: "let v: Array = [{ bar: 'bar' }];", - output: "let v: fooName.BarType[] = [{ bar: 'bar' }];", - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'fooName.BarType', }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: "let v: fooName.BarType[] = [{ bar: 'bar' }];", }, { code: "let w: fooName.BazType[] = [['baz']];", - output: "let w: Array> = [['baz']];", - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringGenericSimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 8, + messageId: 'errorStringGenericSimple', }, ], + options: [{ default: 'array-simple' }], + output: "let w: Array> = [['baz']];", }, { code: 'let x: Array = [undefined] as undefined[];', - output: 'let x: undefined[] = [undefined] as undefined[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'undefined' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let x: undefined[] = [undefined] as undefined[];', }, { code: "let y: string[] = >['2'];", - output: "let y: string[] = ['2'];", - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 20, data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 1, - column: 20, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: "let y: string[] = ['2'];", }, { code: "let z: Array = [3, '4'];", - output: "let z: any[] = [3, '4'];", - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: "let z: any[] = [3, '4'];", }, { code: 'type Arr = Array;', - output: 'type Arr = T[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 15, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 15, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'type Arr = T[];', }, { code: ` // Ignore user defined aliases let yyyy: Arr>[]> = [[[['2']]]]; `, - output: ` -// Ignore user defined aliases -let yyyy: Arr[][]> = [[[['2']]]]; - `, - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 15, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, - column: 15, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: ` +// Ignore user defined aliases +let yyyy: Arr[][]> = [[[['2']]]]; + `, }, { code: ` @@ -1557,6 +1558,15 @@ interface ArrayClass { baz: Arr; } `, + errors: [ + { + column: 8, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, + line: 3, + messageId: 'errorStringArray', + }, + ], + options: [{ default: 'array' }], output: ` interface ArrayClass { foo: T[]; @@ -1564,15 +1574,6 @@ interface ArrayClass { baz: Arr; } `, - options: [{ default: 'array' }], - errors: [ - { - messageId: 'errorStringArray', - data: { className: 'Array', readonlyPrefix: '', type: 'T' }, - line: 3, - column: 8, - }, - ], }, { code: ` @@ -1580,168 +1581,168 @@ function fooFunction(foo: Array>) { return foo.map(e => e.foo); } `, - output: ` -function fooFunction(foo: ArrayClass[]) { - return foo.map(e => e.foo); -} - `, - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 27, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 2, - column: 27, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: ` +function fooFunction(foo: ArrayClass[]) { + return foo.map(e => e.foo); +} + `, }, { code: 'let fooVar: Array<(c: number) => number>;', - output: 'let fooVar: ((c: number) => number)[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 13, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 13, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let fooVar: ((c: number) => number)[];', }, { code: 'type fooUnion = Array;', - output: 'type fooUnion = (string | number | boolean)[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 17, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 17, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'type fooUnion = (string | number | boolean)[];', }, { code: 'type fooIntersection = Array;', - output: 'type fooIntersection = (string & number)[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 24, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 24, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'type fooIntersection = (string & number)[];', }, { code: 'let x: Array;', - output: 'let x: any[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let x: any[];', }, { code: 'let x: Array<>;', - output: 'let x: any[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, - column: 8, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'let x: any[];', }, { code: 'let x: Array;', - output: 'let x: any[];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'any' }, line: 1, - column: 8, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let x: any[];', }, { code: 'let x: Array<>;', - output: 'let x: any[];', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimple', - line: 1, column: 8, + line: 1, + messageId: 'errorStringArraySimple', }, ], + options: [{ default: 'array-simple' }], + output: 'let x: any[];', }, { code: 'let x: Array = [1] as number[];', - output: 'let x: Array = [1] as Array;', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 31, data: { className: 'Array', readonlyPrefix: '', type: 'number' }, line: 1, - column: 31, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let x: Array = [1] as Array;', }, { code: "let y: string[] = >['2'];", - output: "let y: Array = >['2'];", - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 8, data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 1, - column: 8, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: "let y: Array = >['2'];", }, { code: "let ya = [[1, '2']] as [number, string][];", - output: "let ya = [[1, '2']] as Array<[number, string]>;", - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 24, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 24, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: "let ya = [[1, '2']] as Array<[number, string]>;", }, { code: ` // Ignore user defined aliases let yyyy: Arr>[]> = [[[['2']]]]; `, - output: ` -// Ignore user defined aliases -let yyyy: Arr>>> = [[[['2']]]]; - `, - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 15, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 3, - column: 15, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: ` +// Ignore user defined aliases +let yyyy: Arr>>> = [[[['2']]]]; + `, }, { code: ` @@ -1751,6 +1752,15 @@ interface ArrayClass { baz: Arr; } `, + errors: [ + { + column: 8, + data: { className: 'Array', readonlyPrefix: '', type: 'T' }, + line: 4, + messageId: 'errorStringGeneric', + }, + ], + options: [{ default: 'generic' }], output: ` interface ArrayClass { foo: Array; @@ -1758,15 +1768,6 @@ interface ArrayClass { baz: Arr; } `, - options: [{ default: 'generic' }], - errors: [ - { - messageId: 'errorStringGeneric', - data: { className: 'Array', readonlyPrefix: '', type: 'T' }, - line: 4, - column: 8, - }, - ], }, { code: ` @@ -1774,59 +1775,59 @@ function barFunction(bar: ArrayClass[]) { return bar.map(e => e.bar); } `, - output: ` -function barFunction(bar: Array>) { - return bar.map(e => e.bar); -} - `, - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 27, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 2, - column: 27, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: ` +function barFunction(bar: Array>) { + return bar.map(e => e.bar); +} + `, }, { code: 'let barVar: ((c: number) => number)[];', - output: 'let barVar: Array<(c: number) => number>;', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 13, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 13, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'let barVar: Array<(c: number) => number>;', }, { code: 'type barUnion = (string | number | boolean)[];', - output: 'type barUnion = Array;', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 17, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 17, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'type barUnion = Array;', }, { code: 'type barIntersection = (string & number)[];', - output: 'type barIntersection = Array;', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 24, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 24, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'type barIntersection = Array;', }, { code: ` @@ -1834,125 +1835,125 @@ interface FooInterface { '.bar': { baz: string[] }; } `, - output: ` -interface FooInterface { - '.bar': { baz: Array }; -} - `, - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 18, data: { className: 'Array', readonlyPrefix: '', type: 'string' }, line: 3, - column: 18, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: ` +interface FooInterface { + '.bar': { baz: Array }; +} + `, }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 code: 'type Unwrap = T extends Array ? E : T;', - output: 'type Unwrap = T extends (infer E)[] ? E : T;', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 28, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 28, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'type Unwrap = T extends (infer E)[] ? E : T;', }, { // https://github.com/typescript-eslint/typescript-eslint/issues/172 code: 'type Unwrap = T extends (infer E)[] ? E : T;', - output: 'type Unwrap = T extends Array ? E : T;', - options: [{ default: 'generic' }], errors: [ { - messageId: 'errorStringGeneric', + column: 28, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 28, + messageId: 'errorStringGeneric', }, ], + options: [{ default: 'generic' }], + output: 'type Unwrap = T extends Array ? E : T;', }, { code: 'type Foo = ReadonlyArray[];', - output: 'type Foo = (readonly object[])[];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 12, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'object', }, line: 1, - column: 12, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'type Foo = (readonly object[])[];', }, { code: 'const foo: Array void> = [];', - output: 'const foo: (new (...args: any[]) => void)[] = [];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 12, data: { className: 'Array', readonlyPrefix: '', type: 'T' }, line: 1, - column: 12, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'const foo: (new (...args: any[]) => void)[] = [];', }, { code: 'const foo: ReadonlyArray void> = [];', - output: 'const foo: readonly (new (...args: any[]) => void)[] = [];', - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArray', + column: 12, data: { className: 'ReadonlyArray', readonlyPrefix: 'readonly ', type: 'T', }, line: 1, - column: 12, + messageId: 'errorStringArray', }, ], + options: [{ default: 'array' }], + output: 'const foo: readonly (new (...args: any[]) => void)[] = [];', }, { code: "const x: Readonly = ['a', 'b'];", - output: "const x: readonly string[] = ['a', 'b'];", - options: [{ default: 'array' }], errors: [ { - messageId: 'errorStringArrayReadonly', data: { className: 'Readonly', readonlyPrefix: 'readonly ', type: 'string[]', }, + messageId: 'errorStringArrayReadonly', }, ], + options: [{ default: 'array' }], + output: "const x: readonly string[] = ['a', 'b'];", }, { code: 'declare function foo>(extra: E): E;', - output: 'declare function foo(extra: E): E;', - options: [{ default: 'array-simple' }], errors: [ { - messageId: 'errorStringArraySimpleReadonly', data: { className: 'Readonly', readonlyPrefix: 'readonly ', type: 'string[]', }, + messageId: 'errorStringArraySimpleReadonly', }, ], + options: [{ default: 'array-simple' }], + output: 'declare function foo(extra: E): E;', }, ], }); @@ -1975,13 +1976,13 @@ describe('array-type (nested)', () => { const result = linter.verifyAndFix( code, { + parser: '@typescript-eslint/parser', rules: { 'array-type': [ 2, { default: defaultOption, readonly: readonlyOption }, ], }, - parser: '@typescript-eslint/parser', }, { fix: true, diff --git a/packages/eslint-plugin/tests/rules/await-thenable.test.ts b/packages/eslint-plugin/tests/rules/await-thenable.test.ts index 7d88bea824a0..c91542819bb8 100644 --- a/packages/eslint-plugin/tests/rules/await-thenable.test.ts +++ b/packages/eslint-plugin/tests/rules/await-thenable.test.ts @@ -9,8 +9,8 @@ const messageId = 'await'; const ruleTester = new RuleTester({ languageOptions: { parserOptions: { - tsconfigRootDir: rootDir, project: './tsconfig.json', + tsconfigRootDir: rootDir, }, }, }); @@ -217,6 +217,13 @@ async function forAwait() { for await (const value of anee) { console.log(value); } +} + `, + }, + { + code: ` +declare const asyncIter: AsyncIterable | Iterable; +for await (const s of asyncIter) { } `, }, @@ -413,11 +420,11 @@ for await (const value of yieldNumbers()) { `, errors: [ { - messageId: 'forAwaitOfNonThenable', - line: 7, - endLine: 7, column: 1, endColumn: 42, + endLine: 7, + line: 7, + messageId: 'forAwaitOfNonThenable', suggestions: [ { messageId: 'convertToOrdinaryFor', diff --git a/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts b/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts index bfc2a4565700..451702496740 100644 --- a/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts @@ -57,8 +57,8 @@ ruleTester.run('ts-expect-error', rule, { code: '// @ts-expect-error exactly 21 characters', options: [ { - 'ts-expect-error': 'allow-with-description', minimumDescriptionLength: 21, + 'ts-expect-error': 'allow-with-description', }, ], }, @@ -69,8 +69,8 @@ ruleTester.run('ts-expect-error', rule, { `, options: [ { - 'ts-expect-error': 'allow-with-description', minimumDescriptionLength: 21, + 'ts-expect-error': 'allow-with-description', }, ], }, @@ -78,10 +78,10 @@ ruleTester.run('ts-expect-error', rule, { code: '// @ts-expect-error: TS1234 because xyz', options: [ { + minimumDescriptionLength: 10, 'ts-expect-error': { descriptionFormat: '^: TS\\d+ because .+$', }, - minimumDescriptionLength: 10, }, ], }, @@ -92,10 +92,10 @@ ruleTester.run('ts-expect-error', rule, { `, options: [ { + minimumDescriptionLength: 10, 'ts-expect-error': { descriptionFormat: '^: TS\\d+ because .+$', }, - minimumDescriptionLength: 10, }, ], }, @@ -111,90 +111,90 @@ ruleTester.run('ts-expect-error', rule, { invalid: [ { code: '// @ts-expect-error', - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: '/* @ts-expect-error */', - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: ` /* @ts-expect-error */ `, - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 2, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: ` /** on the last line @ts-expect-error */ `, - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 2, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: ` /** on the last line * @ts-expect-error */ `, - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 2, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: ` /** * @ts-expect-error: TODO */ `, - options: [ - { - 'ts-expect-error': 'allow-with-description', - minimumDescriptionLength: 10, - }, - ], errors: [ { + column: 1, data: { directive: 'expect-error', minimumDescriptionLength: 10 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 2, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', + }, + ], + options: [ + { + minimumDescriptionLength: 10, + 'ts-expect-error': 'allow-with-description', }, ], }, @@ -203,20 +203,20 @@ ruleTester.run('ts-expect-error', rule, { /** * @ts-expect-error: TS1234 because xyz */ `, + errors: [ + { + column: 1, + data: { directive: 'expect-error', minimumDescriptionLength: 25 }, + line: 2, + messageId: 'tsDirectiveCommentRequiresDescription', + }, + ], options: [ { + minimumDescriptionLength: 25, 'ts-expect-error': { descriptionFormat: '^: TS\\d+ because .+$', }, - minimumDescriptionLength: 25, - }, - ], - errors: [ - { - data: { directive: 'expect-error', minimumDescriptionLength: 25 }, - messageId: 'tsDirectiveCommentRequiresDescription', - line: 2, - column: 1, }, ], }, @@ -225,6 +225,14 @@ ruleTester.run('ts-expect-error', rule, { /** * @ts-expect-error: TS1234 */ `, + errors: [ + { + column: 1, + data: { directive: 'expect-error', format: '^: TS\\d+ because .+$' }, + line: 2, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', + }, + ], options: [ { 'ts-expect-error': { @@ -232,20 +240,20 @@ ruleTester.run('ts-expect-error', rule, { }, }, ], - errors: [ - { - data: { directive: 'expect-error', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', - line: 2, - column: 1, - }, - ], }, { code: ` /** * @ts-expect-error : TS1234 */ `, + errors: [ + { + column: 1, + data: { directive: 'expect-error', format: '^: TS\\d+ because .+$' }, + line: 2, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', + }, + ], options: [ { 'ts-expect-error': { @@ -253,69 +261,61 @@ ruleTester.run('ts-expect-error', rule, { }, }, ], - errors: [ - { - data: { directive: 'expect-error', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', - line: 2, - column: 1, - }, - ], }, { code: ` /** * @ts-expect-error 👨‍👩‍👧‍👦 */ `, - options: [ - { - 'ts-expect-error': 'allow-with-description', - }, - ], errors: [ { + column: 1, data: { directive: 'expect-error', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 2, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', + }, + ], + options: [ + { + 'ts-expect-error': 'allow-with-description', }, ], }, { code: '/** @ts-expect-error */', - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: '// @ts-expect-error: Suppress next line', - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: '/////@ts-expect-error: Suppress next line', - options: [{ 'ts-expect-error': true }], errors: [ { + column: 1, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: ` @@ -324,88 +324,78 @@ if (false) { console.log('hello'); } `, - options: [{ 'ts-expect-error': true }], errors: [ { + column: 3, data: { directive: 'expect-error' }, - messageId: 'tsDirectiveComment', line: 3, - column: 3, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-expect-error': true }], }, { code: '// @ts-expect-error', - options: [ - { - 'ts-expect-error': 'allow-with-description', - }, - ], errors: [ { + column: 1, data: { directive: 'expect-error', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], - }, - { - code: '// @ts-expect-error: TODO', options: [ { 'ts-expect-error': 'allow-with-description', - minimumDescriptionLength: 10, }, ], + }, + { + code: '// @ts-expect-error: TODO', errors: [ { + column: 1, data: { directive: 'expect-error', minimumDescriptionLength: 10 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], - }, - { - code: '// @ts-expect-error: TS1234 because xyz', options: [ { - 'ts-expect-error': { - descriptionFormat: '^: TS\\d+ because .+$', - }, - minimumDescriptionLength: 25, + minimumDescriptionLength: 10, + 'ts-expect-error': 'allow-with-description', }, ], + }, + { + code: '// @ts-expect-error: TS1234 because xyz', errors: [ { + column: 1, data: { directive: 'expect-error', minimumDescriptionLength: 25 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], - }, - { - code: '// @ts-expect-error: TS1234', options: [ { + minimumDescriptionLength: 25, 'ts-expect-error': { descriptionFormat: '^: TS\\d+ because .+$', }, }, ], + }, + { + code: '// @ts-expect-error: TS1234', errors: [ { + column: 1, data: { directive: 'expect-error', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-expect-error : TS1234 because xyz', options: [ { 'ts-expect-error': { @@ -413,28 +403,38 @@ if (false) { }, }, ], + }, + { + code: '// @ts-expect-error : TS1234 because xyz', errors: [ { + column: 1, data: { directive: 'expect-error', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-expect-error 👨‍👩‍👧‍👦', options: [ { - 'ts-expect-error': 'allow-with-description', + 'ts-expect-error': { + descriptionFormat: '^: TS\\d+ because .+$', + }, }, ], + }, + { + code: '// @ts-expect-error 👨‍👩‍👧‍👦', errors: [ { + column: 1, data: { directive: 'expect-error', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', + }, + ], + options: [ + { + 'ts-expect-error': 'allow-with-description', }, ], }, @@ -460,8 +460,8 @@ ruleTester.run('ts-ignore', rule, { `, options: [ { - 'ts-ignore': 'allow-with-description', minimumDescriptionLength: 21, + 'ts-ignore': 'allow-with-description', }, ], }, @@ -492,10 +492,10 @@ ruleTester.run('ts-ignore', rule, { code: '// @ts-ignore: TS1234 because xyz', options: [ { + minimumDescriptionLength: 10, 'ts-ignore': { descriptionFormat: '^: TS\\d+ because .+$', }, - minimumDescriptionLength: 10, }, ], }, @@ -522,8 +522,8 @@ ruleTester.run('ts-ignore', rule, { code: '// @ts-ignore exactly 21 characters', options: [ { - 'ts-ignore': 'allow-with-description', minimumDescriptionLength: 21, + 'ts-ignore': 'allow-with-description', }, ], }, @@ -534,8 +534,8 @@ ruleTester.run('ts-ignore', rule, { `, options: [ { - 'ts-ignore': 'allow-with-description', minimumDescriptionLength: 21, + 'ts-ignore': 'allow-with-description', }, ], }, @@ -546,10 +546,10 @@ ruleTester.run('ts-ignore', rule, { `, options: [ { + minimumDescriptionLength: 10, 'ts-ignore': { descriptionFormat: '^: TS\\d+ because .+$', }, - minimumDescriptionLength: 10, }, ], }, @@ -557,12 +557,11 @@ ruleTester.run('ts-ignore', rule, { invalid: [ { code: '// @ts-ignore', - options: [{ 'ts-ignore': true, 'ts-expect-error': true }], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 1, column: 1, + line: 1, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -571,17 +570,15 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [{ 'ts-expect-error': true, 'ts-ignore': true }], }, { code: '// @ts-ignore', - options: [ - { 'ts-ignore': true, 'ts-expect-error': 'allow-with-description' }, - ], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 1, column: 1, + line: 1, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -590,14 +587,17 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [ + { 'ts-expect-error': 'allow-with-description', 'ts-ignore': true }, + ], }, { code: '// @ts-ignore', errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 1, column: 1, + line: 1, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -609,12 +609,11 @@ ruleTester.run('ts-ignore', rule, { }, { code: '/* @ts-ignore */', - options: [{ 'ts-ignore': true }], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 1, column: 1, + line: 1, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -623,18 +622,18 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [{ 'ts-ignore': true }], }, { code: ` /* @ts-ignore */ `, - options: [{ 'ts-ignore': true }], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 2, column: 1, + line: 2, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -646,18 +645,18 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [{ 'ts-ignore': true }], }, { code: ` /** on the last line @ts-ignore */ `, - options: [{ 'ts-ignore': true }], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 2, column: 1, + line: 2, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -669,18 +668,18 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [{ 'ts-ignore': true }], }, { code: ` /** on the last line * @ts-ignore */ `, - options: [{ 'ts-ignore': true }], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 2, column: 1, + line: 2, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -692,15 +691,15 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [{ 'ts-ignore': true }], }, { code: '/** @ts-ignore */', - options: [{ 'ts-ignore': true, 'ts-expect-error': false }], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 1, column: 1, + line: 1, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -709,23 +708,18 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [{ 'ts-expect-error': false, 'ts-ignore': true }], }, { code: ` /** * @ts-ignore: TODO */ `, - options: [ - { - 'ts-expect-error': 'allow-with-description', - minimumDescriptionLength: 10, - }, - ], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 2, column: 1, + line: 2, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -737,25 +731,23 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [ + { + minimumDescriptionLength: 10, + 'ts-expect-error': 'allow-with-description', + }, + ], }, { code: ` /** * @ts-ignore: TS1234 because xyz */ `, - options: [ - { - 'ts-expect-error': { - descriptionFormat: '^: TS\\d+ because .+$', - }, - minimumDescriptionLength: 25, - }, - ], errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 2, column: 1, + line: 2, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -767,14 +759,22 @@ ruleTester.run('ts-ignore', rule, { ], }, ], + options: [ + { + minimumDescriptionLength: 25, + 'ts-expect-error': { + descriptionFormat: '^: TS\\d+ because .+$', + }, + }, + ], }, { code: '// @ts-ignore: Suppress next line', errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 1, column: 1, + line: 1, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -788,9 +788,9 @@ ruleTester.run('ts-ignore', rule, { code: '/////@ts-ignore: Suppress next line', errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 1, column: 1, + line: 1, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -809,9 +809,9 @@ if (false) { `, errors: [ { - messageId: 'tsIgnoreInsteadOfExpectError', - line: 3, column: 3, + line: 3, + messageId: 'tsIgnoreInsteadOfExpectError', suggestions: [ { messageId: 'replaceTsIgnoreWithTsExpectError', @@ -828,79 +828,69 @@ if (false) { }, { code: '// @ts-ignore', - options: [{ 'ts-ignore': 'allow-with-description' }], errors: [ { + column: 1, data: { directive: 'ignore', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], + options: [{ 'ts-ignore': 'allow-with-description' }], }, { code: noFormat`// @ts-ignore `, - options: [{ 'ts-ignore': 'allow-with-description' }], errors: [ { + column: 1, data: { directive: 'ignore', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], + options: [{ 'ts-ignore': 'allow-with-description' }], }, { code: '// @ts-ignore .', - options: [{ 'ts-ignore': 'allow-with-description' }], errors: [ { + column: 1, data: { directive: 'ignore', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], + options: [{ 'ts-ignore': 'allow-with-description' }], }, { code: '// @ts-ignore: TS1234 because xyz', - options: [ - { - 'ts-ignore': { - descriptionFormat: '^: TS\\d+ because .+$', - }, - minimumDescriptionLength: 25, - }, - ], errors: [ { + column: 1, data: { directive: 'ignore', minimumDescriptionLength: 25 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], - }, - { - code: '// @ts-ignore: TS1234', options: [ { + minimumDescriptionLength: 25, 'ts-ignore': { descriptionFormat: '^: TS\\d+ because .+$', }, }, ], + }, + { + code: '// @ts-ignore: TS1234', errors: [ { + column: 1, data: { directive: 'ignore', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-ignore : TS1234 because xyz', options: [ { 'ts-ignore': { @@ -908,28 +898,38 @@ if (false) { }, }, ], + }, + { + code: '// @ts-ignore : TS1234 because xyz', errors: [ { + column: 1, data: { directive: 'ignore', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-ignore 👨‍👩‍👧‍👦', options: [ { - 'ts-ignore': 'allow-with-description', + 'ts-ignore': { + descriptionFormat: '^: TS\\d+ because .+$', + }, }, ], + }, + { + code: '// @ts-ignore 👨‍👩‍👧‍👦', errors: [ { + column: 1, data: { directive: 'ignore', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', + }, + ], + options: [ + { + 'ts-ignore': 'allow-with-description', }, ], }, @@ -955,8 +955,8 @@ ruleTester.run('ts-nocheck', rule, { `, options: [ { - 'ts-nocheck': 'allow-with-description', minimumDescriptionLength: 21, + 'ts-nocheck': 'allow-with-description', }, ], }, @@ -964,10 +964,10 @@ ruleTester.run('ts-nocheck', rule, { code: '// @ts-nocheck: TS1234 because xyz', options: [ { + minimumDescriptionLength: 10, 'ts-nocheck': { descriptionFormat: '^: TS\\d+ because .+$', }, - minimumDescriptionLength: 10, }, ], }, @@ -1004,24 +1004,24 @@ const b: string = a; invalid: [ { code: '// @ts-nocheck', - options: [{ 'ts-nocheck': true }], errors: [ { + column: 1, data: { directive: 'nocheck' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-nocheck': true }], }, { code: '// @ts-nocheck', errors: [ { + column: 1, data: { directive: 'nocheck' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], }, @@ -1029,64 +1029,54 @@ const b: string = a; code: '// @ts-nocheck: Suppress next line', errors: [ { + column: 1, data: { directive: 'nocheck' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], }, { code: '// @ts-nocheck', - options: [{ 'ts-nocheck': 'allow-with-description' }], errors: [ { + column: 1, data: { directive: 'nocheck', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], + options: [{ 'ts-nocheck': 'allow-with-description' }], }, { code: '// @ts-nocheck: TS1234 because xyz', - options: [ - { - 'ts-nocheck': { - descriptionFormat: '^: TS\\d+ because .+$', - }, - minimumDescriptionLength: 25, - }, - ], errors: [ { + column: 1, data: { directive: 'nocheck', minimumDescriptionLength: 25 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], - }, - { - code: '// @ts-nocheck: TS1234', options: [ { + minimumDescriptionLength: 25, 'ts-nocheck': { descriptionFormat: '^: TS\\d+ because .+$', }, }, ], + }, + { + code: '// @ts-nocheck: TS1234', errors: [ { + column: 1, data: { directive: 'nocheck', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-nocheck : TS1234 because xyz', options: [ { 'ts-nocheck': { @@ -1094,28 +1084,38 @@ const b: string = a; }, }, ], + }, + { + code: '// @ts-nocheck : TS1234 because xyz', errors: [ { + column: 1, data: { directive: 'nocheck', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-nocheck 👨‍👩‍👧‍👦', options: [ { - 'ts-nocheck': 'allow-with-description', + 'ts-nocheck': { + descriptionFormat: '^: TS\\d+ because .+$', + }, }, ], + }, + { + code: '// @ts-nocheck 👨‍👩‍👧‍👦', errors: [ { + column: 1, data: { directive: 'nocheck', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', + }, + ], + options: [ + { + 'ts-nocheck': 'allow-with-description', }, ], }, @@ -1128,10 +1128,10 @@ const a: true = false; `, errors: [ { + column: 2, data: { directive: 'nocheck', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveComment', line: 2, - column: 2, + messageId: 'tsDirectiveComment', }, ], }, @@ -1153,17 +1153,17 @@ ruleTester.run('ts-check', rule, { { code: '// @ts-check with a description and also with a no-op // @ts-ignore', options: [ - { 'ts-check': 'allow-with-description', minimumDescriptionLength: 3 }, + { minimumDescriptionLength: 3, 'ts-check': 'allow-with-description' }, ], }, { code: '// @ts-check: TS1234 because xyz', options: [ { + minimumDescriptionLength: 10, 'ts-check': { descriptionFormat: '^: TS\\d+ because .+$', }, - minimumDescriptionLength: 10, }, ], }, @@ -1207,27 +1207,27 @@ ruleTester.run('ts-check', rule, { invalid: [ { code: '// @ts-check', - options: [{ 'ts-check': true }], errors: [ { + column: 1, data: { directive: 'check' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-check': true }], }, { code: '// @ts-check: Suppress next line', - options: [{ 'ts-check': true }], errors: [ { + column: 1, data: { directive: 'check' }, - messageId: 'tsDirectiveComment', line: 1, - column: 1, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-check': true }], }, { code: ` @@ -1236,67 +1236,57 @@ if (false) { console.log('hello'); } `, - options: [{ 'ts-check': true }], errors: [ { + column: 3, data: { directive: 'check' }, - messageId: 'tsDirectiveComment', line: 3, - column: 3, + messageId: 'tsDirectiveComment', }, ], + options: [{ 'ts-check': true }], }, { code: '// @ts-check', - options: [{ 'ts-check': 'allow-with-description' }], errors: [ { + column: 1, data: { directive: 'check', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], + options: [{ 'ts-check': 'allow-with-description' }], }, { code: '// @ts-check: TS1234 because xyz', - options: [ - { - 'ts-check': { - descriptionFormat: '^: TS\\d+ because .+$', - }, - minimumDescriptionLength: 25, - }, - ], errors: [ { + column: 1, data: { directive: 'check', minimumDescriptionLength: 25 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', }, ], - }, - { - code: '// @ts-check: TS1234', options: [ { + minimumDescriptionLength: 25, 'ts-check': { descriptionFormat: '^: TS\\d+ because .+$', }, }, ], + }, + { + code: '// @ts-check: TS1234', errors: [ { + column: 1, data: { directive: 'check', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-check : TS1234 because xyz', options: [ { 'ts-check': { @@ -1304,28 +1294,38 @@ if (false) { }, }, ], + }, + { + code: '// @ts-check : TS1234 because xyz', errors: [ { + column: 1, data: { directive: 'check', format: '^: TS\\d+ because .+$' }, - messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', line: 1, - column: 1, + messageId: 'tsDirectiveCommentDescriptionNotMatchPattern', }, ], - }, - { - code: '// @ts-check 👨‍👩‍👧‍👦', options: [ { - 'ts-check': 'allow-with-description', + 'ts-check': { + descriptionFormat: '^: TS\\d+ because .+$', + }, }, ], + }, + { + code: '// @ts-check 👨‍👩‍👧‍👦', errors: [ { + column: 1, data: { directive: 'check', minimumDescriptionLength: 3 }, - messageId: 'tsDirectiveCommentRequiresDescription', line: 1, - column: 1, + messageId: 'tsDirectiveCommentRequiresDescription', + }, + ], + options: [ + { + 'ts-check': 'allow-with-description', }, ], }, diff --git a/packages/eslint-plugin/tests/rules/ban-tslint-comment.test.ts b/packages/eslint-plugin/tests/rules/ban-tslint-comment.test.ts index 062fcd531988..70ebf3d6f107 100644 --- a/packages/eslint-plugin/tests/rules/ban-tslint-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/ban-tslint-comment.test.ts @@ -4,10 +4,10 @@ import rule from '../../src/rules/ban-tslint-comment'; interface Testable { code: string; - text?: string; column?: number; line?: number; output?: string; + text?: string; } const PALANTIR_EXAMPLES: Testable[] = [ @@ -22,9 +22,9 @@ const PALANTIR_EXAMPLES: Testable[] = [ { code: '// tslint:disable-next-line' }, // Disables all rules for the following line { code: 'someCode(); // tslint:disable-line', - text: '// tslint:disable-line', column: 13, output: 'someCode();', + text: '// tslint:disable-line', }, // Disables all rules for the current line { code: '// tslint:disable-next-line:rule1 rule2 rule3...', @@ -38,11 +38,11 @@ const MORE_EXAMPLES: Testable[] = [ // tslint:disable-line console.log(woah); `, + line: 2, output: `const woah = doSomeStuff(); console.log(woah); `, text: '// tslint:disable-line', - line: 2, }, ] @@ -69,15 +69,15 @@ ruleTester.run('ban-tslint-comment', rule, { invalid: [...PALANTIR_EXAMPLES, ...MORE_EXAMPLES].map( ({ code, column, line, output, text }) => ({ code, - output: output ?? '', errors: [ { column: column ?? 1, - line: line ?? 1, data: { text: text ?? code }, + line: line ?? 1, messageId: 'commentDetected' as const, }, ], + output: output ?? '', }), ), }); diff --git a/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts b/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts index 902274292572..df8a7ad66d45 100644 --- a/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts +++ b/packages/eslint-plugin/tests/rules/class-literal-property-style.test.ts @@ -269,9 +269,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 7, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -295,9 +295,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 7, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -321,9 +321,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 14, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -347,9 +347,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 21, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -373,9 +373,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 15, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -399,9 +399,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 15, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -423,9 +423,9 @@ class Mx { `, errors: [ { - messageId: 'preferGetterStyle', column: 20, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -448,9 +448,9 @@ class Mx { `, errors: [ { - messageId: 'preferGetterStyle', column: 12, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -473,9 +473,9 @@ class Mx { `, errors: [ { - messageId: 'preferGetterStyle', column: 12, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -498,9 +498,9 @@ class Mx { `, errors: [ { - messageId: 'preferGetterStyle', column: 19, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -525,9 +525,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 17, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -550,9 +550,9 @@ class Mx { `, errors: [ { - messageId: 'preferGetterStyle', column: 22, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -577,9 +577,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 21, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -601,9 +601,9 @@ class Mx { `, errors: [ { - messageId: 'preferGetterStyle', column: 26, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -635,9 +635,9 @@ class Mx { `, errors: [ { - messageId: 'preferFieldStyle', column: 14, line: 3, + messageId: 'preferFieldStyle', suggestions: [ { messageId: 'preferFieldStyleSuggestion', @@ -673,9 +673,9 @@ class Mx { `, errors: [ { - messageId: 'preferGetterStyle', column: 19, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -711,12 +711,11 @@ class A { } } `, - options: ['getters'], errors: [ { - messageId: 'preferGetterStyle', column: 20, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -737,6 +736,7 @@ class A { ], }, ], + options: ['getters'], }, { code: ` @@ -754,12 +754,11 @@ class A { } } `, - options: ['getters'], errors: [ { - messageId: 'preferGetterStyle', column: 24, line: 6, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -782,6 +781,7 @@ class A { ], }, ], + options: ['getters'], }, { code: ` @@ -794,12 +794,11 @@ class A { } } `, - options: ['getters'], errors: [ { - messageId: 'preferGetterStyle', column: 20, line: 3, + messageId: 'preferGetterStyle', suggestions: [ { messageId: 'preferGetterStyleSuggestion', @@ -817,6 +816,7 @@ class A { ], }, ], + options: ['getters'], }, ], }); diff --git a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this-core.test.ts b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this-core.test.ts index 50f527082553..d0193efdc99d 100644 --- a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this-core.test.ts +++ b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this-core.test.ts @@ -8,440 +8,440 @@ import rule from '../../../src/rules/class-methods-use-this'; const ruleTester = new RuleTester(); ruleTester.run('class-methods-use-this', rule, { - valid: [ - { - code: 'class A { constructor() {} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { foo() {this} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: "class A { foo() {this.bar = 'bar';} }", - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { foo() {bar(this);} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A extends B { foo() {super.foo();} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { foo() { if(true) { return this; } } }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { static foo() {} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: '({ a(){} });', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { foo() { () => this; } }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: '({ a: function () {} });', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { foo() {this} bar() {} }', - options: [{ exceptMethods: ['bar'] }], - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { "foo"() { } }', - options: [{ exceptMethods: ['foo'] }], - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { 42() { } }', - options: [{ exceptMethods: ['42'] }], - languageOptions: { parserOptions: { ecmaVersion: 6 } }, - }, - { - code: 'class A { foo = function() {this} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { foo = () => {this} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { foo = () => {super.toString} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { static foo = function() {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { static foo = () => {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { #bar() {} }', - options: [{ exceptMethods: ['#bar'] }], - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { foo = function () {} }', - options: [{ enforceForClassFields: false }], - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { foo = () => {} }', - options: [{ enforceForClassFields: false }], - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { foo() { return class { [this.foo] = 1 }; } }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - { - code: 'class A { static {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, - }, - ], invalid: [ { code: 'class A { foo() {} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: 'class A { foo() {/**this**/} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: 'class A { foo() {var a = function () {this};} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: 'class A { foo() {var a = function () {var b = function(){this}};} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: 'class A { foo() {window.this} }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: "class A { foo() {that.this = 'this';} }", - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: 'class A { foo() { () => undefined; } }', - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: 'class A { foo() {} bar() {} }', - options: [{ exceptMethods: ['bar'] }], - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + options: [{ exceptMethods: ['bar'] }], }, { code: 'class A { foo() {} hasOwnProperty() {} }', - options: [{ exceptMethods: ['foo'] }], - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 20, - messageId: 'missingThis', data: { name: "method 'hasOwnProperty'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + options: [{ exceptMethods: ['foo'] }], }, { code: 'class A { [foo]() {} }', - options: [{ exceptMethods: ['foo'] }], - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 11, - messageId: 'missingThis', data: { name: 'method' }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + options: [{ exceptMethods: ['foo'] }], }, { code: 'class A { #foo() { } foo() {} #bar() {} }', - options: [{ exceptMethods: ['#foo'] }], - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 22, - messageId: 'missingThis', data: { name: "method 'foo'" }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, { - type: AST_NODE_TYPES.FunctionExpression, - line: 1, column: 31, - messageId: 'missingThis', data: { name: 'private method #bar' }, + line: 1, + messageId: 'missingThis', + type: AST_NODE_TYPES.FunctionExpression, }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + options: [{ exceptMethods: ['#foo'] }], }, { code: "class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} }", - languageOptions: { parserOptions: { ecmaVersion: 6 } }, errors: [ { - messageId: 'missingThis', + column: 11, data: { name: "method 'foo'" }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 11, }, { - messageId: 'missingThis', + column: 19, data: { name: "method 'bar'" }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 19, }, { - messageId: 'missingThis', + column: 29, data: { name: "method '123'" }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 29, }, { - messageId: 'missingThis', + column: 37, data: { name: "method 'baz'" }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 37, }, { - messageId: 'missingThis', + column: 49, data: { name: 'method' }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 49, }, { - messageId: 'missingThis', + column: 57, data: { name: 'method' }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 57, }, { - messageId: 'missingThis', + column: 68, data: { name: "getter 'quux'" }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 68, }, { - messageId: 'missingThis', + column: 81, data: { name: 'setter' }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 81, }, { - messageId: 'missingThis', + column: 93, data: { name: "generator method 'quuux'" }, + messageId: 'missingThis', type: AST_NODE_TYPES.FunctionExpression, - column: 93, }, ], + languageOptions: { parserOptions: { ecmaVersion: 6 } }, }, { code: 'class A { foo = function() {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: "method 'foo'" }, column: 11, + data: { name: "method 'foo'" }, endColumn: 25, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { foo = () => {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: "method 'foo'" }, column: 11, + data: { name: "method 'foo'" }, endColumn: 17, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { #foo = function() {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: 'private method #foo' }, column: 11, + data: { name: 'private method #foo' }, endColumn: 26, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { #foo = () => {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: 'private method #foo' }, column: 11, + data: { name: 'private method #foo' }, endColumn: 18, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { #foo() {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: 'private method #foo' }, column: 11, + data: { name: 'private method #foo' }, endColumn: 15, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { get #foo() {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: 'private getter #foo' }, column: 11, + data: { name: 'private getter #foo' }, endColumn: 19, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { set #foo(x) {} }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: 'private setter #foo' }, column: 11, + data: { name: 'private setter #foo' }, endColumn: 19, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { foo () { return class { foo = this }; } }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: "method 'foo'" }, column: 11, + data: { name: "method 'foo'" }, endColumn: 15, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { foo () { return function () { foo = this }; } }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: "method 'foo'" }, column: 11, + data: { name: "method 'foo'" }, endColumn: 15, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, { code: 'class A { foo () { return class { static { this; } } } }', - languageOptions: { parserOptions: { ecmaVersion: 2022 } }, errors: [ { - messageId: 'missingThis', - data: { name: "method 'foo'" }, column: 11, + data: { name: "method 'foo'" }, endColumn: 15, + messageId: 'missingThis', }, ], + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + }, + ], + valid: [ + { + code: 'class A { constructor() {} }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: 'class A { foo() {this} }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: "class A { foo() {this.bar = 'bar';} }", + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: 'class A { foo() {bar(this);} }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: 'class A extends B { foo() {super.foo();} }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: 'class A { foo() { if(true) { return this; } } }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: 'class A { static foo() {} }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: '({ a(){} });', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: 'class A { foo() { () => this; } }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: '({ a: function () {} });', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + }, + { + code: 'class A { foo() {this} bar() {} }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + options: [{ exceptMethods: ['bar'] }], + }, + { + code: 'class A { "foo"() { } }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + options: [{ exceptMethods: ['foo'] }], + }, + { + code: 'class A { 42() { } }', + languageOptions: { parserOptions: { ecmaVersion: 6 } }, + options: [{ exceptMethods: ['42'] }], + }, + { + code: 'class A { foo = function() {this} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + }, + { + code: 'class A { foo = () => {this} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + }, + { + code: 'class A { foo = () => {super.toString} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + }, + { + code: 'class A { static foo = function() {} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + }, + { + code: 'class A { static foo = () => {} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + }, + { + code: 'class A { #bar() {} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + options: [{ exceptMethods: ['#bar'] }], + }, + { + code: 'class A { foo = function () {} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + options: [{ enforceForClassFields: false }], + }, + { + code: 'class A { foo = () => {} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + options: [{ enforceForClassFields: false }], + }, + { + code: 'class A { foo() { return class { [this.foo] = 1 }; } }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, + }, + { + code: 'class A { static {} }', + languageOptions: { parserOptions: { ecmaVersion: 2022 } }, }, ], }); diff --git a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts index 575daef11a8b..4238d4c05582 100644 --- a/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts +++ b/packages/eslint-plugin/tests/rules/class-methods-use-this/class-methods-use-this.test.ts @@ -5,536 +5,523 @@ import rule from '../../../src/rules/class-methods-use-this'; const ruleTester = new RuleTester(); ruleTester.run('class-methods-use-this', rule, { - valid: [ + invalid: [ { code: ` -class Foo implements Bar { +class Foo { method() {} } `, - options: [{ ignoreClassesThatImplementAnInterface: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` -class Foo implements Bar { - get getter() {} +class Foo { + private method() {} } `, - options: [{ ignoreClassesThatImplementAnInterface: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` -class Foo implements Bar { - set setter() {} +class Foo { + protected method() {} } `, - options: [{ ignoreClassesThatImplementAnInterface: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - override method() {} + #method() {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - private override method() {} + get getter(): number {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - protected override method() {} + private get getter(): number {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - override get getter(): number {} + protected get getter(): number {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - private override get getter(): number {} + get #getter(): number {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - protected override get getter(): number {} + set setter(b: number) {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [ + { + ignoreClassesThatImplementAnInterface: false, + ignoreOverrideMethods: false, + }, + ], }, { code: ` class Foo { - override set setter(v: number) {} + private set setter(b: number) {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - private override set setter(v: number) {} + protected set setter(b: number) {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo { - protected override set setter(v: number) {} + set #setter(b: number) {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{}], }, { code: ` class Foo implements Bar { - override method() {} + method() {} } `, - options: [ + errors: [ { - ignoreClassesThatImplementAnInterface: true, - ignoreOverrideMethods: true, + messageId: 'missingThis', }, ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` class Foo implements Bar { - private override method() {} + #method() {} } `, - options: [ + errors: [ { - // _interface_ cannot have `private`/`protected` modifier on members. - // We should ignore only public members. - ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, + messageId: 'missingThis', }, ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` class Foo implements Bar { - protected override method() {} + private method() {} } `, + errors: [ + { + messageId: 'missingThis', + }, + ], options: [ { // _interface_ cannot have `private`/`protected` modifier on members. // We should ignore only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - override get getter(): number {} + protected method() {} } `, - options: [ + errors: [ { - ignoreClassesThatImplementAnInterface: true, - ignoreOverrideMethods: true, + messageId: 'missingThis', }, ], - }, - { - code: ` -class Foo implements Bar { - private override get getter(): number {} -} - `, options: [ { // _interface_ cannot have `private`/`protected` modifier on members. // We should ignore only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - protected override get getter(): number {} + get getter(): number {} } `, - options: [ + errors: [ { - // _interface_ cannot have `private`/`protected` modifier on members. - // We should ignore only public members. - ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, + messageId: 'missingThis', }, ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` class Foo implements Bar { - override set setter(v: number) {} + get #getter(): number {} } `, - options: [ + errors: [ { - ignoreClassesThatImplementAnInterface: true, - ignoreOverrideMethods: true, + messageId: 'missingThis', }, ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` class Foo implements Bar { - private override set setter(v: number) {} + private get getter(): number {} } `, + errors: [ + { + messageId: 'missingThis', + }, + ], options: [ { // _interface_ cannot have `private`/`protected` modifier on members. // We should ignore only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - protected override set setter(v: number) {} + protected get getter(): number {} } `, + errors: [ + { + messageId: 'missingThis', + }, + ], options: [ { // _interface_ cannot have `private`/`protected` modifier on members. // We should ignore only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - property = () => {}; -} - `, - options: [{ ignoreClassesThatImplementAnInterface: true }], - }, - { - code: ` -class Foo { - override property = () => {}; -} - `, - options: [{ ignoreOverrideMethods: true }], - }, - { - code: ` -class Foo { - private override property = () => {}; + set setter(v: number) {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` -class Foo { - protected override property = () => {}; +class Foo implements Bar { + set #setter(v: number) {} } `, - options: [{ ignoreOverrideMethods: true }], + errors: [ + { + messageId: 'missingThis', + }, + ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` class Foo implements Bar { - override property = () => {}; + private set setter(v: number) {} } `, + errors: [ + { + messageId: 'missingThis', + }, + ], options: [ { - ignoreClassesThatImplementAnInterface: true, - ignoreOverrideMethods: true, + // _interface_ cannot have `private`/`protected` modifier on members. + // We should ignore only public members. + ignoreClassesThatImplementAnInterface: 'public-fields', + ignoreOverrideMethods: false, }, ], }, { code: ` class Foo implements Bar { - property = () => {}; + protected set setter(v: number) {} } `, - options: [ + errors: [ { - ignoreClassesThatImplementAnInterface: false, - enforceForClassFields: false, + messageId: 'missingThis', }, ], - }, - { - code: ` -class Foo { - override property = () => {}; -} - `, - options: [ - { - ignoreOverrideMethods: false, - enforceForClassFields: false, - }, - ], - }, - { - code: ` -class Foo implements Bar { - private override property = () => {}; -} - `, - options: [ - { - // _interface_ cannot have `private`/`protected` modifier on members. - // We should check only public members. - ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, - }, - ], - }, - { - code: ` -class Foo implements Bar { - protected override property = () => {}; -} - `, options: [ { // _interface_ cannot have `private`/`protected` modifier on members. - // We should check only public members. + // We should ignore only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - // But overridden properties should be ignored. - ignoreOverrideMethods: true, + ignoreOverrideMethods: false, }, ], }, - ], - invalid: [ { code: ` class Foo { - method() {} + override method() {} } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], + options: [{ ignoreOverrideMethods: false }], }, { code: ` class Foo { - private method() {} + override get getter(): number {} } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], + options: [{ ignoreOverrideMethods: false }], }, { code: ` class Foo { - protected method() {} + override set setter(v: number) {} } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], + options: [{ ignoreOverrideMethods: false }], }, { code: ` -class Foo { - #method() {} +class Foo implements Bar { + override method() {} } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], - }, - { - code: ` -class Foo { - get getter(): number {} -} - `, - options: [{}], - errors: [ + options: [ { - messageId: 'missingThis', + ignoreClassesThatImplementAnInterface: false, + ignoreOverrideMethods: false, }, ], }, { code: ` -class Foo { - private get getter(): number {} +class Foo implements Bar { + override get getter(): number {} } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], - }, - { - code: ` -class Foo { - protected get getter(): number {} -} - `, - options: [{}], - errors: [ + options: [ { - messageId: 'missingThis', + ignoreClassesThatImplementAnInterface: false, + ignoreOverrideMethods: false, }, ], }, { code: ` -class Foo { - get #getter(): number {} +class Foo implements Bar { + override set setter(v: number) {} } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], - }, - { - code: ` -class Foo { - set setter(b: number) {} -} - `, options: [ { ignoreClassesThatImplementAnInterface: false, ignoreOverrideMethods: false, }, ], - errors: [ - { - messageId: 'missingThis', - }, - ], }, { code: ` -class Foo { - private set setter(b: number) {} +class Foo implements Bar { + property = () => {}; } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` -class Foo { - protected set setter(b: number) {} +class Foo implements Bar { + #property = () => {}; } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], + options: [{ ignoreClassesThatImplementAnInterface: false }], }, { code: ` class Foo { - set #setter(b: number) {} + override property = () => {}; } `, - options: [{}], errors: [ { messageId: 'missingThis', }, ], + options: [{ ignoreOverrideMethods: false }], }, { code: ` class Foo implements Bar { - method() {} + override property = () => {}; } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], errors: [ { messageId: 'missingThis', }, ], + options: [ + { + ignoreClassesThatImplementAnInterface: false, + ignoreOverrideMethods: false, + }, + ], }, { code: ` class Foo implements Bar { - #method() {} + private property = () => {}; } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], errors: [ { messageId: 'missingThis', }, ], - }, - { - code: ` -class Foo implements Bar { - private method() {} -} - `, options: [ { // _interface_ cannot have `private`/`protected` modifier on members. @@ -542,18 +529,18 @@ class Foo implements Bar { ignoreClassesThatImplementAnInterface: 'public-fields', }, ], - errors: [ - { - messageId: 'missingThis', - }, - ], }, { code: ` class Foo implements Bar { - protected method() {} + protected property = () => {}; } `, + errors: [ + { + messageId: 'missingThis', + }, + ], options: [ { // _interface_ cannot have `private`/`protected` modifier on members. @@ -561,106 +548,122 @@ class Foo implements Bar { ignoreClassesThatImplementAnInterface: 'public-fields', }, ], - errors: [ - { - messageId: 'missingThis', - }, - ], }, + ], + valid: [ { code: ` class Foo implements Bar { - get getter(): number {} + method() {} } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], - errors: [ - { - messageId: 'missingThis', - }, - ], + options: [{ ignoreClassesThatImplementAnInterface: true }], }, { code: ` class Foo implements Bar { - get #getter(): number {} + get getter() {} } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], - errors: [ - { - messageId: 'missingThis', - }, - ], + options: [{ ignoreClassesThatImplementAnInterface: true }], }, { code: ` class Foo implements Bar { - private get getter(): number {} + set setter() {} } `, - options: [ - { - // _interface_ cannot have `private`/`protected` modifier on members. - // We should ignore only public members. - ignoreClassesThatImplementAnInterface: 'public-fields', - }, - ], - errors: [ - { - messageId: 'missingThis', - }, - ], + options: [{ ignoreClassesThatImplementAnInterface: true }], }, { code: ` -class Foo implements Bar { - protected get getter(): number {} +class Foo { + override method() {} } `, - options: [ - { - // _interface_ cannot have `private`/`protected` modifier on members. - // We should ignore only public members. - ignoreClassesThatImplementAnInterface: 'public-fields', - }, - ], - errors: [ - { - messageId: 'missingThis', - }, - ], + options: [{ ignoreOverrideMethods: true }], }, { code: ` -class Foo implements Bar { - set setter(v: number) {} +class Foo { + private override method() {} } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], - errors: [ - { - messageId: 'missingThis', - }, - ], + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + protected override method() {} +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + override get getter(): number {} +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + private override get getter(): number {} +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + protected override get getter(): number {} +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + override set setter(v: number) {} +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + private override set setter(v: number) {} +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + protected override set setter(v: number) {} +} + `, + options: [{ ignoreOverrideMethods: true }], }, { code: ` class Foo implements Bar { - set #setter(v: number) {} + override method() {} } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], - errors: [ + options: [ { - messageId: 'missingThis', + ignoreClassesThatImplementAnInterface: true, + ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - private set setter(v: number) {} + private override method() {} } `, options: [ @@ -668,19 +671,15 @@ class Foo implements Bar { // _interface_ cannot have `private`/`protected` modifier on members. // We should ignore only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - ignoreOverrideMethods: false, - }, - ], - errors: [ - { - messageId: 'missingThis', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - protected set setter(v: number) {} + protected override method() {} } `, options: [ @@ -688,105 +687,98 @@ class Foo implements Bar { // _interface_ cannot have `private`/`protected` modifier on members. // We should ignore only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - ignoreOverrideMethods: false, - }, - ], - errors: [ - { - messageId: 'missingThis', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, { code: ` -class Foo { - override method() {} +class Foo implements Bar { + override get getter(): number {} } `, - options: [{ ignoreOverrideMethods: false }], - errors: [ + options: [ { - messageId: 'missingThis', + ignoreClassesThatImplementAnInterface: true, + ignoreOverrideMethods: true, }, ], }, { code: ` -class Foo { - override get getter(): number {} +class Foo implements Bar { + private override get getter(): number {} } `, - options: [{ ignoreOverrideMethods: false }], - errors: [ + options: [ { - messageId: 'missingThis', + // _interface_ cannot have `private`/`protected` modifier on members. + // We should ignore only public members. + ignoreClassesThatImplementAnInterface: 'public-fields', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, { code: ` -class Foo { - override set setter(v: number) {} +class Foo implements Bar { + protected override get getter(): number {} } `, - options: [{ ignoreOverrideMethods: false }], - errors: [ + options: [ { - messageId: 'missingThis', + // _interface_ cannot have `private`/`protected` modifier on members. + // We should ignore only public members. + ignoreClassesThatImplementAnInterface: 'public-fields', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - override method() {} + override set setter(v: number) {} } `, options: [ { - ignoreClassesThatImplementAnInterface: false, - ignoreOverrideMethods: false, - }, - ], - errors: [ - { - messageId: 'missingThis', + ignoreClassesThatImplementAnInterface: true, + ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - override get getter(): number {} + private override set setter(v: number) {} } `, options: [ { - ignoreClassesThatImplementAnInterface: false, - ignoreOverrideMethods: false, - }, - ], - errors: [ - { - messageId: 'missingThis', + // _interface_ cannot have `private`/`protected` modifier on members. + // We should ignore only public members. + ignoreClassesThatImplementAnInterface: 'public-fields', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - override set setter(v: number) {} + protected override set setter(v: number) {} } `, options: [ { - ignoreClassesThatImplementAnInterface: false, - ignoreOverrideMethods: false, - }, - ], - errors: [ - { - messageId: 'missingThis', + // _interface_ cannot have `private`/`protected` modifier on members. + // We should ignore only public members. + ignoreClassesThatImplementAnInterface: 'public-fields', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, @@ -796,92 +788,100 @@ class Foo implements Bar { property = () => {}; } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], - errors: [ - { - messageId: 'missingThis', - }, - ], + options: [{ ignoreClassesThatImplementAnInterface: true }], }, { code: ` -class Foo implements Bar { - #property = () => {}; +class Foo { + override property = () => {}; } `, - options: [{ ignoreClassesThatImplementAnInterface: false }], - errors: [ - { - messageId: 'missingThis', - }, - ], + options: [{ ignoreOverrideMethods: true }], }, { code: ` class Foo { + private override property = () => {}; +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo { + protected override property = () => {}; +} + `, + options: [{ ignoreOverrideMethods: true }], + }, + { + code: ` +class Foo implements Bar { override property = () => {}; } `, - options: [{ ignoreOverrideMethods: false }], - errors: [ + options: [ { - messageId: 'missingThis', + ignoreClassesThatImplementAnInterface: true, + ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - override property = () => {}; + property = () => {}; } `, options: [ { + enforceForClassFields: false, ignoreClassesThatImplementAnInterface: false, - ignoreOverrideMethods: false, }, ], - errors: [ + }, + { + code: ` +class Foo { + override property = () => {}; +} + `, + options: [ { - messageId: 'missingThis', + enforceForClassFields: false, + ignoreOverrideMethods: false, }, ], }, { code: ` class Foo implements Bar { - private property = () => {}; + private override property = () => {}; } `, options: [ { // _interface_ cannot have `private`/`protected` modifier on members. - // We should ignore only public members. + // We should check only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - }, - ], - errors: [ - { - messageId: 'missingThis', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, { code: ` class Foo implements Bar { - protected property = () => {}; + protected override property = () => {}; } `, options: [ { // _interface_ cannot have `private`/`protected` modifier on members. - // We should ignore only public members. + // We should check only public members. ignoreClassesThatImplementAnInterface: 'public-fields', - }, - ], - errors: [ - { - messageId: 'missingThis', + // But overridden properties should be ignored. + ignoreOverrideMethods: true, }, ], }, diff --git a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts index f7f50ea44524..bf49ce384a14 100644 --- a/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-generic-constructors.test.ts @@ -321,72 +321,72 @@ const a = function (a = new Foo()) {}; }, { code: 'const a = new Foo();', - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: 'const a: Foo = new Foo();', }, { code: 'const a = new Map();', - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: 'const a: Map = new Map();', }, { code: noFormat`const a = new Map ();`, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: `const a: Map = new Map ();`, }, { code: noFormat`const a = new Map< string, number >();`, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: `const a: Map< string, number > = new Map();`, }, { code: noFormat`const a = new \n Foo \n ();`, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: `const a: Foo = new \n Foo \n ();`, }, { code: 'const a = new Foo/* comment */ /* another */();', - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: `const a: Foo = new Foo/* comment */ /* another */();`, }, { code: 'const a = new Foo();', - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: `const a: Foo = new Foo();`, }, { @@ -395,12 +395,12 @@ class Foo { a = new Foo(); } `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` class Foo { a: Foo = new Foo(); @@ -413,12 +413,12 @@ class Foo { [a] = new Foo(); } `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` class Foo { [a]: Foo = new Foo(); @@ -431,12 +431,12 @@ class Foo { [a + b] = new Foo(); } `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` class Foo { [a + b]: Foo = new Foo(); @@ -447,12 +447,12 @@ class Foo { code: ` function foo(a = new Foo()) {} `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` function foo(a: Foo = new Foo()) {} `, @@ -461,12 +461,12 @@ function foo(a: Foo = new Foo()) {} code: ` function foo({ a } = new Foo()) {} `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` function foo({ a }: Foo = new Foo()) {} `, @@ -475,12 +475,12 @@ function foo({ a }: Foo = new Foo()) {} code: ` function foo([a] = new Foo()) {} `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` function foo([a]: Foo = new Foo()) {} `, @@ -491,12 +491,12 @@ class A { constructor(a = new Foo()) {} } `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` class A { constructor(a: Foo = new Foo()) {} @@ -507,12 +507,12 @@ class A { code: ` const a = function (a = new Foo()) {}; `, - options: ['type-annotation'], errors: [ { messageId: 'preferTypeAnnotation', }, ], + options: ['type-annotation'], output: ` const a = function (a: Foo = new Foo()) {}; `, diff --git a/packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts b/packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts index 30f98f976e5c..b6d4e8dff432 100644 --- a/packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-indexed-object-style.test.ts @@ -150,10 +150,10 @@ interface Foo { [key: string]: any; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Record; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Readonly interface @@ -163,10 +163,10 @@ interface Foo { readonly [key: string]: any; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Readonly>; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Interface with generic parameter @@ -176,10 +176,10 @@ interface Foo { [key: string]: A; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Record; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Interface with generic parameter and default value @@ -189,10 +189,10 @@ interface Foo { [key: string]: A; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Record; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Interface with extends @@ -202,8 +202,8 @@ interface B extends A { [index: number]: unknown; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: null, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Readonly interface with generic parameter { @@ -212,10 +212,10 @@ interface Foo { readonly [key: string]: A; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Readonly>; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Interface with multiple generic parameters @@ -225,10 +225,10 @@ interface Foo { [key: A]: B; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Record; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Readonly interface with multiple generic parameters @@ -238,101 +238,101 @@ interface Foo { readonly [key: A]: B; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Readonly>; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, // Type literal { code: 'type Foo = { [key: string]: any };', + errors: [{ column: 12, line: 1, messageId: 'preferRecord' }], output: 'type Foo = Record;', - errors: [{ messageId: 'preferRecord', line: 1, column: 12 }], }, // Readonly type literal { code: 'type Foo = { readonly [key: string]: any };', + errors: [{ column: 12, line: 1, messageId: 'preferRecord' }], output: 'type Foo = Readonly>;', - errors: [{ messageId: 'preferRecord', line: 1, column: 12 }], }, // Generic { code: 'type Foo = Generic<{ [key: string]: any }>;', + errors: [{ column: 20, line: 1, messageId: 'preferRecord' }], output: 'type Foo = Generic>;', - errors: [{ messageId: 'preferRecord', line: 1, column: 20 }], }, // Readonly Generic { code: 'type Foo = Generic<{ readonly [key: string]: any }>;', + errors: [{ column: 20, line: 1, messageId: 'preferRecord' }], output: 'type Foo = Generic>>;', - errors: [{ messageId: 'preferRecord', line: 1, column: 20 }], }, // Function types { code: 'function foo(arg: { [key: string]: any }) {}', + errors: [{ column: 19, line: 1, messageId: 'preferRecord' }], output: 'function foo(arg: Record) {}', - errors: [{ messageId: 'preferRecord', line: 1, column: 19 }], }, { code: 'function foo(): { [key: string]: any } {}', + errors: [{ column: 17, line: 1, messageId: 'preferRecord' }], output: 'function foo(): Record {}', - errors: [{ messageId: 'preferRecord', line: 1, column: 17 }], }, // Readonly function types { code: 'function foo(arg: { readonly [key: string]: any }) {}', + errors: [{ column: 19, line: 1, messageId: 'preferRecord' }], output: 'function foo(arg: Readonly>) {}', - errors: [{ messageId: 'preferRecord', line: 1, column: 19 }], }, { code: 'function foo(): { readonly [key: string]: any } {}', + errors: [{ column: 17, line: 1, messageId: 'preferRecord' }], output: 'function foo(): Readonly> {}', - errors: [{ messageId: 'preferRecord', line: 1, column: 17 }], }, // Never // Type literal { code: 'type Foo = Record;', + errors: [{ column: 12, line: 1, messageId: 'preferIndexSignature' }], options: ['index-signature'], output: 'type Foo = { [key: string]: any };', - errors: [{ messageId: 'preferIndexSignature', line: 1, column: 12 }], }, // Type literal with generic parameter { code: 'type Foo = Record;', + errors: [{ column: 15, line: 1, messageId: 'preferIndexSignature' }], options: ['index-signature'], output: 'type Foo = { [key: string]: T };', - errors: [{ messageId: 'preferIndexSignature', line: 1, column: 15 }], }, // Circular { code: 'type Foo = { [k: string]: A.Foo };', + errors: [{ column: 12, line: 1, messageId: 'preferRecord' }], output: 'type Foo = Record;', - errors: [{ messageId: 'preferRecord', line: 1, column: 12 }], }, { code: 'type Foo = { [key: string]: AnotherFoo };', + errors: [{ column: 12, line: 1, messageId: 'preferRecord' }], output: 'type Foo = Record;', - errors: [{ messageId: 'preferRecord', line: 1, column: 12 }], }, { code: 'type Foo = { [key: string]: { [key: string]: Foo } };', + errors: [{ column: 29, line: 1, messageId: 'preferRecord' }], output: 'type Foo = { [key: string]: Record };', - errors: [{ messageId: 'preferRecord', line: 1, column: 29 }], }, { code: 'type Foo = { [key: string]: string } | Foo;', + errors: [{ column: 12, line: 1, messageId: 'preferRecord' }], output: 'type Foo = Record | Foo;', - errors: [{ messageId: 'preferRecord', line: 1, column: 12 }], }, { code: ` @@ -340,10 +340,10 @@ interface Foo { [k: string]: T; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Record; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, { code: ` @@ -351,10 +351,10 @@ interface Foo { [k: string]: A.Foo; } `, + errors: [{ column: 1, line: 2, messageId: 'preferRecord' }], output: ` type Foo = Record; `, - errors: [{ messageId: 'preferRecord', line: 2, column: 1 }], }, { code: ` @@ -362,34 +362,34 @@ interface Foo { [k: string]: { [key: string]: Foo }; } `, + errors: [{ column: 16, line: 3, messageId: 'preferRecord' }], output: ` interface Foo { [k: string]: Record; } `, - errors: [{ messageId: 'preferRecord', line: 3, column: 16 }], }, // Generic { code: 'type Foo = Generic>;', + errors: [{ column: 20, line: 1, messageId: 'preferIndexSignature' }], options: ['index-signature'], output: 'type Foo = Generic<{ [key: string]: any }>;', - errors: [{ messageId: 'preferIndexSignature', line: 1, column: 20 }], }, // Function types { code: 'function foo(arg: Record) {}', + errors: [{ column: 19, line: 1, messageId: 'preferIndexSignature' }], options: ['index-signature'], output: 'function foo(arg: { [key: string]: any }) {}', - errors: [{ messageId: 'preferIndexSignature', line: 1, column: 19 }], }, { code: 'function foo(): Record {}', + errors: [{ column: 17, line: 1, messageId: 'preferIndexSignature' }], options: ['index-signature'], output: 'function foo(): { [key: string]: any } {}', - errors: [{ messageId: 'preferIndexSignature', line: 1, column: 17 }], }, ], }); diff --git a/packages/eslint-plugin/tests/rules/consistent-return.test.ts b/packages/eslint-plugin/tests/rules/consistent-return.test.ts index b26b70654dee..e62345932396 100644 --- a/packages/eslint-plugin/tests/rules/consistent-return.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-return.test.ts @@ -8,8 +8,8 @@ const rootDir = getFixturesRootDir(); const ruleTester = new RuleTester({ languageOptions: { parserOptions: { - tsconfigRootDir: rootDir, project: './tsconfig.json', + tsconfigRootDir: rootDir, }, }, }); @@ -220,13 +220,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'missingReturnValue', - data: { name: "Function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 4, column: 16, - endLine: 4, + data: { name: "Function 'foo'" }, endColumn: 23, + endLine: 4, + line: 4, + messageId: 'missingReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -240,13 +240,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'missingReturnValue', - data: { name: "Function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 5, column: 11, - endLine: 5, + data: { name: "Function 'foo'" }, endColumn: 18, + endLine: 5, + line: 5, + messageId: 'missingReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -264,22 +264,22 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'unexpectedReturnValue', - data: { name: "Function 'baz'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 6, column: 13, - endLine: 6, + data: { name: "Function 'baz'" }, endColumn: 30, + endLine: 6, + line: 6, + messageId: 'unexpectedReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, { - messageId: 'missingReturnValue', - data: { name: "Function 'bar'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 9, column: 11, - endLine: 9, + data: { name: "Function 'bar'" }, endColumn: 18, + endLine: 9, + line: 9, + messageId: 'missingReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -292,13 +292,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'missingReturnValue', - data: { name: "Function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 4, column: 16, - endLine: 4, + data: { name: "Function 'foo'" }, endColumn: 23, + endLine: 4, + line: 4, + messageId: 'missingReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -311,13 +311,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'unexpectedReturnValue', - data: { name: "Async function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 4, column: 16, - endLine: 4, + data: { name: "Async function 'foo'" }, endColumn: 31, + endLine: 4, + line: 4, + messageId: 'unexpectedReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -330,13 +330,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'missingReturnValue', - data: { name: "Async function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 4, column: 16, - endLine: 4, + data: { name: "Async function 'foo'" }, endColumn: 23, + endLine: 4, + line: 4, + messageId: 'missingReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -349,13 +349,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'unexpectedReturnValue', - data: { name: "Async function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 4, column: 11, - endLine: 4, + data: { name: "Async function 'foo'" }, endColumn: 20, + endLine: 4, + line: 4, + messageId: 'unexpectedReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -368,13 +368,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'unexpectedReturnValue', - data: { name: "Function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 4, column: 16, - endLine: 4, + data: { name: "Function 'foo'" }, endColumn: 31, + endLine: 4, + line: 4, + messageId: 'unexpectedReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -390,13 +390,13 @@ ruleTester.run('consistent-return', rule, { `, errors: [ { - messageId: 'missingReturnValue', - data: { name: "Function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 7, column: 11, - endLine: 7, + data: { name: "Function 'foo'" }, endColumn: 18, + endLine: 7, + line: 7, + messageId: 'missingReturnValue', + type: AST_NODE_TYPES.ReturnStatement, }, ], }, @@ -409,20 +409,20 @@ ruleTester.run('consistent-return', rule, { return true; } `, - options: [ - { - treatUndefinedAsUnspecified: true, - }, - ], errors: [ { - messageId: 'unexpectedReturnValue', - data: { name: "Function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 6, column: 11, - endLine: 6, + data: { name: "Function 'foo'" }, endColumn: 23, + endLine: 6, + line: 6, + messageId: 'unexpectedReturnValue', + type: AST_NODE_TYPES.ReturnStatement, + }, + ], + options: [ + { + treatUndefinedAsUnspecified: true, }, ], }, @@ -436,20 +436,20 @@ ruleTester.run('consistent-return', rule, { return undefOrNum; } `, - options: [ - { - treatUndefinedAsUnspecified: true, - }, - ], errors: [ { - messageId: 'unexpectedReturnValue', - data: { name: "Function 'foo'" }, - type: AST_NODE_TYPES.ReturnStatement, - line: 7, column: 11, - endLine: 7, + data: { name: "Function 'foo'" }, endColumn: 29, + endLine: 7, + line: 7, + messageId: 'unexpectedReturnValue', + type: AST_NODE_TYPES.ReturnStatement, + }, + ], + options: [ + { + treatUndefinedAsUnspecified: true, }, ], }, diff --git a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts index c9373af01984..efd24bd7c4ba 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-assertions.test.ts @@ -1,13 +1,15 @@ /* eslint-disable @typescript-eslint/no-deprecated -- TODO - migrate this test away from `batchedSingleLineTests` */ +import type { TSESTree } from '@typescript-eslint/utils'; + import * as parser from '@typescript-eslint/parser'; import { RuleTester } from '@typescript-eslint/rule-tester'; -import type { TSESTree } from '@typescript-eslint/utils'; import type { MessageIds, Options, } from '../../src/rules/consistent-type-assertions'; + import rule from '../../src/rules/consistent-type-assertions'; import { dedupeTestCases } from '../dedupeTestCases'; import { batchedSingleLineTests } from '../RuleTester'; @@ -168,445 +170,445 @@ ruleTester.run('consistent-type-assertions', rule, { ).flatMap(([assertionStyle, code, output]) => batchedSingleLineTests({ code, - options: [{ assertionStyle }], errors: code .split(`\n`) - .map((_, i) => ({ messageId: assertionStyle, line: i + 1 })), + .map((_, i) => ({ line: i + 1, messageId: assertionStyle })), + options: [{ assertionStyle }], output, }), ), ), ...batchedSingleLineTests({ code: OBJECT_LITERAL_AS_CASTS, - options: [ - { - assertionStyle: 'as', - objectLiteralTypeAssertions: 'allow-as-parameter', - }, - ], errors: [ { - messageId: 'unexpectedObjectTypeAssertion', line: 2, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: Foo = {};', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies Foo;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 3, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: a | b = ({});', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = ({}) satisfies a | b;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 4, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'A' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies A + b;', }, ], }, ], - }), - ...batchedSingleLineTests({ - code: OBJECT_LITERAL_ANGLE_BRACKET_CASTS, options: [ { - assertionStyle: 'angle-bracket', + assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter', }, ], + }), + ...batchedSingleLineTests({ + code: OBJECT_LITERAL_ANGLE_BRACKET_CASTS, errors: [ { - messageId: 'unexpectedObjectTypeAssertion', line: 2, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: Foo = {};', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies Foo;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 3, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: a | b = ({});', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = ({}) satisfies a | b;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 4, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'A' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies A + b;', }, ], }, ], + options: [ + { + assertionStyle: 'angle-bracket', + objectLiteralTypeAssertions: 'allow-as-parameter', + }, + ], }), ...batchedSingleLineTests({ code: `${OBJECT_LITERAL_AS_CASTS.trimEnd()}${OBJECT_LITERAL_ARGUMENT_AS_CASTS}`, - options: [{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }], errors: [ { - messageId: 'unexpectedObjectTypeAssertion', line: 2, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: Foo = {};', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies Foo;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 3, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: a | b = ({});', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = ({}) satisfies a | b;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 4, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'A' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies A + b;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 5, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'print({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 6, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'new print({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 7, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'function foo() { throw { bar: 5 } satisfies Foo }', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 8, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo.Bar' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'function b(x = {} satisfies Foo.Bar) {}', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 9, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'function c(x = {} satisfies Foo) {}', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 10, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'print?.({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 11, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'print?.call({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 12, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: `print\`\${{ bar: 5 } satisfies Foo}\``, }, ], }, ], + options: [{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' }], }), ...batchedSingleLineTests({ code: `${OBJECT_LITERAL_ANGLE_BRACKET_CASTS.trimEnd()}${OBJECT_LITERAL_ARGUMENT_ANGLE_BRACKET_CASTS}`, - options: [ - { - assertionStyle: 'angle-bracket', - objectLiteralTypeAssertions: 'never', - }, - ], errors: [ { - messageId: 'unexpectedObjectTypeAssertion', line: 2, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: Foo = {};', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies Foo;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 3, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithAnnotation', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithAnnotation', output: 'const x: a | b = ({});', }, { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'a | b' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = ({}) satisfies a | b;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 4, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'A' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'const x = {} satisfies A + b;', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 5, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'print({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 6, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'new print({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 7, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'function foo() { throw { bar: 5 } satisfies Foo }', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 8, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'print?.({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 9, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: 'print?.call({ bar: 5 } satisfies Foo)', }, ], }, { - messageId: 'unexpectedObjectTypeAssertion', line: 10, + messageId: 'unexpectedObjectTypeAssertion', suggestions: [ { - messageId: 'replaceObjectTypeAssertionWithSatisfies', data: { cast: 'Foo' }, + messageId: 'replaceObjectTypeAssertionWithSatisfies', output: `print\`\${{ bar: 5 } satisfies Foo}\``, }, ], }, ], + options: [ + { + assertionStyle: 'angle-bracket', + objectLiteralTypeAssertions: 'never', + }, + ], }), { code: 'const foo = ;', - output: null, + errors: [{ line: 1, messageId: 'never' }], languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } }, options: [{ assertionStyle: 'never' }], - errors: [{ messageId: 'never', line: 1 }], + output: null, }, { code: 'const a = (b, c);', - output: `const a = (b, c) as any;`, - options: [ - { - assertionStyle: 'as', - }, - ], errors: [ { - messageId: 'as', line: 1, + messageId: 'as', }, ], - }, - { - code: 'const f = (() => {});', - output: 'const f = (() => {}) as any;', options: [ { assertionStyle: 'as', }, ], + output: `const a = (b, c) as any;`, + }, + { + code: 'const f = (() => {});', errors: [ { - messageId: 'as', line: 1, + messageId: 'as', }, ], - }, - { - code: 'const f = function () {};', - output: 'const f = function () {} as any;', options: [ { assertionStyle: 'as', }, ], + output: 'const f = (() => {}) as any;', + }, + { + code: 'const f = function () {};', errors: [ { - messageId: 'as', line: 1, + messageId: 'as', }, ], - }, - { - code: 'const f = (async () => {});', - output: 'const f = (async () => {}) as any;', options: [ { assertionStyle: 'as', }, ], + output: 'const f = function () {} as any;', + }, + { + code: 'const f = (async () => {});', errors: [ { - messageId: 'as', line: 1, + messageId: 'as', }, ], + options: [ + { + assertionStyle: 'as', + }, + ], + output: 'const f = (async () => {}) as any;', }, { // prettier wants to remove the parens around the yield expression, @@ -616,58 +618,58 @@ function* g() { const y = (yield a); } `, - output: ` -function* g() { - const y = (yield a) as any; -} - `, - options: [ + errors: [ { - assertionStyle: 'as', + line: 3, + messageId: 'as', }, ], - errors: [ + options: [ { - messageId: 'as', - line: 3, + assertionStyle: 'as', }, ], + output: ` +function* g() { + const y = (yield a) as any; +} + `, }, { code: ` declare let x: number, y: number; const bs = (x <<= y); `, - output: ` -declare let x: number, y: number; -const bs = (x <<= y) as any; - `, - options: [ - { - assertionStyle: 'as', - }, - ], errors: [ { - messageId: 'as', line: 3, + messageId: 'as', }, ], - }, - { - code: 'const ternary = (true ? x : y);', - output: 'const ternary = (true ? x : y) as any;', options: [ { assertionStyle: 'as', }, ], + output: ` +declare let x: number, y: number; +const bs = (x <<= y) as any; + `, + }, + { + code: 'const ternary = (true ? x : y);', errors: [ { - messageId: 'as', line: 1, + messageId: 'as', + }, + ], + options: [ + { + assertionStyle: 'as', }, ], + output: 'const ternary = (true ? x : y) as any;', }, ], }); diff --git a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts index 4fa17b04e988..3735fca22ffa 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-definitions.test.ts @@ -62,39 +62,39 @@ export type W = { invalid: [ { code: noFormat`type T = { x: number; };`, - output: `interface T { x: number; }`, - options: ['interface'], errors: [ { - messageId: 'interfaceOverType', - line: 1, column: 6, + line: 1, + messageId: 'interfaceOverType', }, ], + options: ['interface'], + output: `interface T { x: number; }`, }, { code: noFormat`type T={ x: number; };`, - output: `interface T { x: number; }`, - options: ['interface'], errors: [ { - messageId: 'interfaceOverType', - line: 1, column: 6, + line: 1, + messageId: 'interfaceOverType', }, ], + options: ['interface'], + output: `interface T { x: number; }`, }, { code: noFormat`type T= { x: number; };`, - output: `interface T { x: number; }`, - options: ['interface'], errors: [ { - messageId: 'interfaceOverType', - line: 1, column: 6, + line: 1, + messageId: 'interfaceOverType', }, ], + options: ['interface'], + output: `interface T { x: number; }`, }, { code: ` @@ -102,79 +102,79 @@ export type W = { x: T; }; `, - output: ` -export interface W { - x: T; -} - `, - options: ['interface'], errors: [ { - messageId: 'interfaceOverType', - line: 2, column: 13, + line: 2, + messageId: 'interfaceOverType', }, ], + options: ['interface'], + output: ` +export interface W { + x: T; +} + `, }, { code: noFormat`interface T { x: number; }`, - output: `type T = { x: number; }`, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 1, column: 11, + line: 1, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: `type T = { x: number; }`, }, { code: noFormat`interface T{ x: number; }`, - output: `type T = { x: number; }`, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 1, column: 11, + line: 1, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: `type T = { x: number; }`, }, { code: noFormat`interface T { x: number; }`, - output: `type T = { x: number; }`, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 1, column: 11, + line: 1, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: `type T = { x: number; }`, }, { code: noFormat`interface A extends B, C { x: number; };`, - output: `type A = { x: number; } & B & C;`, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 1, column: 11, + line: 1, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: `type A = { x: number; } & B & C;`, }, { code: noFormat`interface A extends B, C { x: number; };`, - output: `type A = { x: number; } & B & C;`, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 1, column: 11, + line: 1, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: `type A = { x: number; } & B & C;`, }, { code: ` @@ -182,19 +182,19 @@ export interface W { x: T; } `, - output: ` -export type W = { - x: T; -} - `, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 2, column: 18, + line: 2, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: ` +export type W = { + x: T; +} + `, }, { code: ` @@ -204,6 +204,14 @@ namespace JSX { } } `, + errors: [ + { + column: 13, + line: 3, + messageId: 'typeOverInterface', + }, + ], + options: ['type'], output: ` namespace JSX { type Array = { @@ -211,14 +219,6 @@ namespace JSX { } } `, - options: ['type'], - errors: [ - { - messageId: 'typeOverInterface', - line: 3, - column: 13, - }, - ], }, { code: ` @@ -228,6 +228,14 @@ global { } } `, + errors: [ + { + column: 13, + line: 3, + messageId: 'typeOverInterface', + }, + ], + options: ['type'], output: ` global { type Array = { @@ -235,14 +243,6 @@ global { } } `, - options: ['type'], - errors: [ - { - messageId: 'typeOverInterface', - line: 3, - column: 13, - }, - ], }, { code: ` @@ -252,15 +252,15 @@ declare global { } } `, - output: null, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 3, column: 13, + line: 3, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: null, }, { code: ` @@ -270,15 +270,15 @@ declare global { } } `, - output: null, - options: ['type'], errors: [ { - messageId: 'typeOverInterface', - line: 4, column: 15, + line: 4, + messageId: 'typeOverInterface', }, ], + options: ['type'], + output: null, }, { // https://github.com/typescript-eslint/typescript-eslint/issues/3894 @@ -288,6 +288,14 @@ export default interface Test { foo(): number; } `, + errors: [ + { + column: 26, + line: 2, + messageId: 'typeOverInterface', + }, + ], + options: ['type'], output: ` type Test = { bar(): string; @@ -295,14 +303,6 @@ type Test = { } export default Test `, - options: ['type'], - errors: [ - { - messageId: 'typeOverInterface', - line: 2, - column: 26, - }, - ], }, { // https://github.com/typescript-eslint/typescript-eslint/issues/4333 @@ -312,20 +312,20 @@ export declare type Test = { bar: string; }; `, + errors: [ + { + column: 21, + line: 2, + messageId: 'interfaceOverType', + }, + ], + options: ['interface'], output: ` export declare interface Test { foo: string; bar: string; } `, - options: ['interface'], - errors: [ - { - messageId: 'interfaceOverType', - line: 2, - column: 21, - }, - ], }, { // https://github.com/typescript-eslint/typescript-eslint/issues/4333 @@ -335,20 +335,20 @@ export declare interface Test { bar: string; } `, + errors: [ + { + column: 26, + line: 2, + messageId: 'typeOverInterface', + }, + ], + options: ['type'], output: ` export declare type Test = { foo: string; bar: string; } `, - options: ['type'], - errors: [ - { - messageId: 'typeOverInterface', - line: 2, - column: 26, - }, - ], }, ], }); diff --git a/packages/eslint-plugin/tests/rules/consistent-type-exports.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-exports.test.ts index 8ccfeb9bf5d5..880f4441047a 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-exports.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-exports.test.ts @@ -8,8 +8,8 @@ const rootDir = getFixturesRootDir(); const ruleTester = new RuleTester({ languageOptions: { parserOptions: { - tsconfigRootDir: rootDir, project: './tsconfig.json', + tsconfigRootDir: rootDir, }, }, }); @@ -66,86 +66,86 @@ export { NonTypeNS }; invalid: [ { code: "export { Type1 } from './consistent-type-exports';", - output: "export type { Type1 } from './consistent-type-exports';", errors: [ { - messageId: 'typeOverValue', - line: 1, column: 1, + line: 1, + messageId: 'typeOverValue', }, ], + output: "export type { Type1 } from './consistent-type-exports';", }, { code: "export { Type1, value1 } from './consistent-type-exports';", - output: - `export type { Type1 } from './consistent-type-exports';\n` + - `export { value1 } from './consistent-type-exports';`, errors: [ { - messageId: 'singleExportIsType', - line: 1, column: 1, + line: 1, + messageId: 'singleExportIsType', }, ], + output: + `export type { Type1 } from './consistent-type-exports';\n` + + `export { value1 } from './consistent-type-exports';`, }, { code: ` export { Type1, value1, value2 } from './consistent-type-exports'; - `, - output: ` -export type { Type1 } from './consistent-type-exports'; -export { value1, value2 } from './consistent-type-exports'; `, errors: [ { - messageId: 'singleExportIsType', - line: 2, column: 1, + line: 2, + messageId: 'singleExportIsType', }, ], + output: ` +export type { Type1 } from './consistent-type-exports'; +export { value1, value2 } from './consistent-type-exports'; + `, }, { code: ` export { Type1, value1, Type2, value2 } from './consistent-type-exports'; - `, - output: ` -export type { Type1, Type2 } from './consistent-type-exports'; -export { value1, value2 } from './consistent-type-exports'; `, errors: [ { - messageId: 'multipleExportsAreTypes', - line: 2, column: 1, + line: 2, + messageId: 'multipleExportsAreTypes', }, ], + output: ` +export type { Type1, Type2 } from './consistent-type-exports'; +export { value1, value2 } from './consistent-type-exports'; + `, }, { code: "export { Type2 as Foo } from './consistent-type-exports';", - output: "export type { Type2 as Foo } from './consistent-type-exports';", errors: [ { - messageId: 'typeOverValue', - line: 1, column: 1, + line: 1, + messageId: 'typeOverValue', }, ], + output: "export type { Type2 as Foo } from './consistent-type-exports';", }, { code: ` export { Type2 as Foo, value1 } from './consistent-type-exports'; - `, - output: ` -export type { Type2 as Foo } from './consistent-type-exports'; -export { value1 } from './consistent-type-exports'; `, errors: [ { - messageId: 'singleExportIsType', - line: 2, column: 1, + line: 2, + messageId: 'singleExportIsType', }, ], + output: ` +export type { Type2 as Foo } from './consistent-type-exports'; +export { value1 } from './consistent-type-exports'; + `, }, { code: ` @@ -154,53 +154,53 @@ export { value1 as BScope, value2 as CScope, } from './consistent-type-exports'; - `, - output: ` -export type { Type2 as Foo } from './consistent-type-exports'; -export { value1 as BScope, value2 as CScope } from './consistent-type-exports'; `, errors: [ { - messageId: 'singleExportIsType', - line: 2, column: 1, + line: 2, + messageId: 'singleExportIsType', }, ], + output: ` +export type { Type2 as Foo } from './consistent-type-exports'; +export { value1 as BScope, value2 as CScope } from './consistent-type-exports'; + `, }, { code: ` import { Type2 } from './consistent-type-exports'; export { Type2 }; - `, - output: ` -import { Type2 } from './consistent-type-exports'; -export type { Type2 }; `, errors: [ { - messageId: 'typeOverValue', - line: 3, column: 1, + line: 3, + messageId: 'typeOverValue', }, ], + output: ` +import { Type2 } from './consistent-type-exports'; +export type { Type2 }; + `, }, { code: ` import { value2, Type2 } from './consistent-type-exports'; export { value2, Type2 }; - `, - output: ` -import { value2, Type2 } from './consistent-type-exports'; -export type { Type2 }; -export { value2 }; `, errors: [ { - messageId: 'singleExportIsType', - line: 3, column: 1, + line: 3, + messageId: 'singleExportIsType', }, ], + output: ` +import { value2, Type2 } from './consistent-type-exports'; +export type { Type2 }; +export { value2 }; + `, }, { code: ` @@ -213,6 +213,13 @@ namespace TypeNS { export { Alias, IFace, TypeNS }; `, + errors: [ + { + column: 1, + line: 9, + messageId: 'multipleExportsAreTypes', + }, + ], output: ` type Alias = 1; interface IFace {} @@ -224,13 +231,6 @@ namespace TypeNS { export type { Alias, IFace }; export { TypeNS }; `, - errors: [ - { - messageId: 'multipleExportsAreTypes', - line: 9, - column: 1, - }, - ], }, { code: ` @@ -240,6 +240,13 @@ namespace TypeNS { export { TypeNS }; `, + errors: [ + { + column: 1, + line: 6, + messageId: 'typeOverValue', + }, + ], output: ` namespace TypeNS { export interface Foo {} @@ -247,47 +254,40 @@ namespace TypeNS { export type { TypeNS }; `, - errors: [ - { - messageId: 'typeOverValue', - line: 6, - column: 1, - }, - ], }, { code: ` type T = 1; export { type T, T }; - `, - output: ` -type T = 1; -export type { T, T }; `, errors: [ { - messageId: 'typeOverValue', - line: 3, column: 1, + line: 3, + messageId: 'typeOverValue', }, ], + output: ` +type T = 1; +export type { T, T }; + `, }, { code: noFormat` type T = 1; export { type/* */T, type /* */T, T }; - `, - output: ` -type T = 1; -export type { /* */T, /* */T, T }; `, errors: [ { - messageId: 'typeOverValue', - line: 3, column: 1, + line: 3, + messageId: 'typeOverValue', }, ], + output: ` +type T = 1; +export type { /* */T, /* */T, T }; + `, }, { code: ` @@ -295,19 +295,19 @@ type T = 1; const x = 1; export { type T, T, x }; `, + errors: [ + { + column: 1, + line: 4, + messageId: 'singleExportIsType', + }, + ], output: ` type T = 1; const x = 1; export type { T, T }; export { x }; `, - errors: [ - { - messageId: 'singleExportIsType', - line: 4, - column: 1, - }, - ], }, { code: ` @@ -315,37 +315,37 @@ type T = 1; const x = 1; export { T, x }; `, - output: ` -type T = 1; -const x = 1; -export { type T, x }; - `, - options: [{ fixMixedExportsWithInlineTypeSpecifier: true }], errors: [ { - messageId: 'singleExportIsType', - line: 4, column: 1, + line: 4, + messageId: 'singleExportIsType', }, ], + options: [{ fixMixedExportsWithInlineTypeSpecifier: true }], + output: ` +type T = 1; +const x = 1; +export { type T, x }; + `, }, { code: ` type T = 1; export { type T, T }; `, - output: ` -type T = 1; -export type { T, T }; - `, - options: [{ fixMixedExportsWithInlineTypeSpecifier: true }], errors: [ { - messageId: 'typeOverValue', - line: 3, column: 1, + line: 3, + messageId: 'typeOverValue', }, ], + options: [{ fixMixedExportsWithInlineTypeSpecifier: true }], + output: ` +type T = 1; +export type { T, T }; + `, }, { code: ` @@ -356,18 +356,18 @@ export { value2 as CScope, } from './consistent-type-exports'; `, - output: ` -export type { Type1, Type2 as Foo, value1 as BScope } from './consistent-type-exports'; -export { value2 as CScope } from './consistent-type-exports'; - `, - options: [{ fixMixedExportsWithInlineTypeSpecifier: false }], errors: [ { - messageId: 'multipleExportsAreTypes', - line: 2, column: 1, + line: 2, + messageId: 'multipleExportsAreTypes', }, ], + options: [{ fixMixedExportsWithInlineTypeSpecifier: false }], + output: ` +export type { Type1, Type2 as Foo, value1 as BScope } from './consistent-type-exports'; +export { value2 as CScope } from './consistent-type-exports'; + `, }, { code: ` @@ -378,6 +378,14 @@ export { value2 as CScope, } from './consistent-type-exports'; `, + errors: [ + { + column: 1, + line: 2, + messageId: 'multipleExportsAreTypes', + }, + ], + options: [{ fixMixedExportsWithInlineTypeSpecifier: true }], output: ` export { type Type1, @@ -386,31 +394,23 @@ export { value2 as CScope, } from './consistent-type-exports'; `, - options: [{ fixMixedExportsWithInlineTypeSpecifier: true }], - errors: [ - { - messageId: 'multipleExportsAreTypes', - line: 2, - column: 1, - }, - ], }, { code: ` export * from './consistent-type-exports/type-only-exports'; `, - output: ` - export type * from './consistent-type-exports/type-only-exports'; - `, errors: [ { column: 9, endColumn: 69, - line: 2, endLine: 2, + line: 2, messageId: 'typeOverValue', }, ], + output: ` + export type * from './consistent-type-exports/type-only-exports'; + `, }, { code: noFormat` @@ -419,55 +419,55 @@ export { // comment 3 from './consistent-type-exports/type-only-exports'; `, - output: ` - /* comment 1 */ export - /* comment 2 */ type * - // comment 3 - from './consistent-type-exports/type-only-exports'; - `, errors: [ { column: 25, endColumn: 64, - line: 2, endLine: 5, + line: 2, messageId: 'typeOverValue', }, ], + output: ` + /* comment 1 */ export + /* comment 2 */ type * + // comment 3 + from './consistent-type-exports/type-only-exports'; + `, }, { code: ` export * from './consistent-type-exports/type-only-reexport'; `, - output: ` - export type * from './consistent-type-exports/type-only-reexport'; - `, errors: [ { column: 9, endColumn: 70, - line: 2, endLine: 2, + line: 2, messageId: 'typeOverValue', }, ], + output: ` + export type * from './consistent-type-exports/type-only-reexport'; + `, }, { code: ` export * as foo from './consistent-type-exports/type-only-reexport'; `, - output: ` - export type * as foo from './consistent-type-exports/type-only-reexport'; - `, errors: [ { column: 9, endColumn: 77, - line: 2, endLine: 2, + line: 2, messageId: 'typeOverValue', }, ], + output: ` + export type * as foo from './consistent-type-exports/type-only-reexport'; + `, }, ], }); diff --git a/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts index 48d918a6436e..f881862c9688 100644 --- a/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts +++ b/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts @@ -4,16 +4,16 @@ import rule from '../../src/rules/consistent-type-imports'; const PARSER_OPTION_COMBOS = [ { - experimentalDecorators: false, emitDecoratorMetadata: false, + experimentalDecorators: false, }, { - experimentalDecorators: false, emitDecoratorMetadata: true, + experimentalDecorators: false, }, { - experimentalDecorators: true, emitDecoratorMetadata: false, + experimentalDecorators: true, }, ]; for (const parserOptions of PARSER_OPTION_COMBOS) { @@ -184,7 +184,7 @@ type Z = C; const b = B; `, options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, ], }, { @@ -195,7 +195,7 @@ type T = A; const b = B; `, options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, ], }, { @@ -206,7 +206,7 @@ type T = A; const b = B; `, options: [ - { prefer: 'no-type-imports', fixStyle: 'inline-type-imports' }, + { fixStyle: 'inline-type-imports', prefer: 'no-type-imports' }, ], }, // exports @@ -389,6 +389,12 @@ interface Baz { } function fn(a: Foo): Foo {} `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; let foo: Foo; @@ -398,148 +404,142 @@ interface Baz { } function fn(a: Foo): Foo {} `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` import Foo from 'foo'; let foo: Foo; `, - output: ` -import type Foo from 'foo'; -let foo: Foo; - `, - options: [{ prefer: 'type-imports' }], errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + options: [{ prefer: 'type-imports' }], + output: ` +import type Foo from 'foo'; +let foo: Foo; + `, }, { code: ` import Foo from 'foo'; let foo: Foo; `, - output: ` -import type Foo from 'foo'; -let foo: Foo; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import type Foo from 'foo'; +let foo: Foo; + `, }, { code: ` import { A, B } from 'foo'; let foo: A; -let bar: B; - `, - output: ` -import type { A, B } from 'foo'; -let foo: A; let bar: B; `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type { A, B } from 'foo'; +let foo: A; +let bar: B; + `, }, { code: ` import { A as a, B as b } from 'foo'; let foo: a; -let bar: b; - `, - output: ` -import type { A as a, B as b } from 'foo'; -let foo: a; let bar: b; `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type { A as a, B as b } from 'foo'; +let foo: a; +let bar: b; + `, }, { code: ` import Foo from 'foo'; -type Bar = typeof Foo; // TSTypeQuery - `, - output: ` -import type Foo from 'foo'; type Bar = typeof Foo; // TSTypeQuery `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type Foo from 'foo'; +type Bar = typeof Foo; // TSTypeQuery + `, }, { code: ` import foo from 'foo'; -type Bar = foo.Bar; // TSQualifiedName - `, - output: ` -import type foo from 'foo'; type Bar = foo.Bar; // TSQualifiedName `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type foo from 'foo'; +type Bar = foo.Bar; // TSQualifiedName + `, }, { code: ` import foo from 'foo'; -type Baz = (typeof foo.bar)['Baz']; // TSQualifiedName & TSTypeQuery - `, - output: ` -import type foo from 'foo'; type Baz = (typeof foo.bar)['Baz']; // TSQualifiedName & TSTypeQuery `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type foo from 'foo'; +type Baz = (typeof foo.bar)['Baz']; // TSQualifiedName & TSTypeQuery + `, }, { code: ` import * as A from 'foo'; -let foo: A.Foo; - `, - output: ` -import type * as A from 'foo'; let foo: A.Foo; `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type * as A from 'foo'; +let foo: A.Foo; + `, }, { // default and named @@ -548,92 +548,92 @@ import A, { B } from 'foo'; let foo: A; let bar: B; `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type { B } from 'foo'; import type A from 'foo'; let foo: A; let bar: B; `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: noFormat` import A, {} from 'foo'; -let foo: A; - `, - output: ` -import type A from 'foo'; let foo: A; `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type A from 'foo'; +let foo: A; + `, }, { code: ` import { A, B } from 'foo'; -const foo: A = B(); - `, - output: ` -import type { A} from 'foo'; -import { B } from 'foo'; const foo: A = B(); `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"A"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { A} from 'foo'; +import { B } from 'foo'; +const foo: A = B(); + `, }, { code: ` import { A, B, C } from 'foo'; const foo: A = B(); -let bar: C; - `, - output: ` -import type { A, C } from 'foo'; -import { B } from 'foo'; -const foo: A = B(); let bar: C; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"A" and "C"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { A, C } from 'foo'; +import { B } from 'foo'; +const foo: A = B(); +let bar: C; + `, }, { code: ` import { A, B, C, D } from 'foo'; const foo: A = B(); -type T = { bar: C; baz: D }; - `, - output: ` -import type { A, C, D } from 'foo'; -import { B } from 'foo'; -const foo: A = B(); type T = { bar: C; baz: D }; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"A", "C" and "D"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { A, C, D } from 'foo'; +import { B } from 'foo'; +const foo: A = B(); +type T = { bar: C; baz: D }; + `, }, { code: ` @@ -641,6 +641,13 @@ import A, { B, C, D } from 'foo'; B(); type T = { foo: A; bar: C; baz: D }; `, + errors: [ + { + data: { typeImports: '"A", "C" and "D"' }, + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], output: ` import type { C, D } from 'foo'; import type A from 'foo'; @@ -648,33 +655,26 @@ import { B } from 'foo'; B(); type T = { foo: A; bar: C; baz: D }; `, - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - data: { typeImports: '"A", "C" and "D"' }, - line: 2, - }, - ], }, { code: ` import A, { B } from 'foo'; B(); -type T = A; - `, - output: ` -import type A from 'foo'; -import { B } from 'foo'; -B(); type T = A; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"A"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type A from 'foo'; +import { B } from 'foo'; +B(); +type T = A; + `, }, { code: ` @@ -683,124 +683,124 @@ import type { Already1 } from 'foo'; import A, { B } from 'foo'; import { C, D, E } from 'bar'; import type { Already2 } from 'bar'; -type T = { b: B; c: C; d: D }; - `, - output: ` -import type Already1Def from 'foo'; -import type { Already1 , B } from 'foo'; -import A from 'foo'; -import { E } from 'bar'; -import type { Already2 , C, D} from 'bar'; type T = { b: B; c: C; d: D }; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"B"' }, line: 4, + messageId: 'someImportsAreOnlyTypes', }, { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"C" and "D"' }, line: 5, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type Already1Def from 'foo'; +import type { Already1 , B } from 'foo'; +import A from 'foo'; +import { E } from 'bar'; +import type { Already2 , C, D} from 'bar'; +type T = { b: B; c: C; d: D }; + `, }, { code: ` import A, { /* comment */ B } from 'foo'; -type T = B; - `, - output: ` -import type { /* comment */ B } from 'foo'; -import A from 'foo'; type T = B; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"B"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { /* comment */ B } from 'foo'; +import A from 'foo'; +type T = B; + `, }, { code: noFormat` import { A, B, C } from 'foo'; import { D, E, F, } from 'bar'; -type T = A | D; - `, - output: ` -import type { A} from 'foo'; -import { B, C } from 'foo'; -import type { D} from 'bar'; -import { E, F, } from 'bar'; type T = A | D; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"A"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"D"' }, line: 3, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { A} from 'foo'; +import { B, C } from 'foo'; +import type { D} from 'bar'; +import { E, F, } from 'bar'; +type T = A | D; + `, }, { code: noFormat` import { A, B, C } from 'foo'; import { D, E, F, } from 'bar'; -type T = B | E; - `, - output: ` -import type { B} from 'foo'; -import { A, C } from 'foo'; -import type { E} from 'bar'; -import { D, F, } from 'bar'; type T = B | E; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"B"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"E"' }, line: 3, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { B} from 'foo'; +import { A, C } from 'foo'; +import type { E} from 'bar'; +import { D, F, } from 'bar'; +type T = B | E; + `, }, { code: noFormat` import { A, B, C } from 'foo'; import { D, E, F, } from 'bar'; -type T = C | F; - `, - output: ` -import type { C } from 'foo'; -import { A, B } from 'foo'; -import type { F} from 'bar'; -import { D, E } from 'bar'; type T = C | F; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"C"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"F"' }, line: 3, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { C } from 'foo'; +import { A, B } from 'foo'; +import type { F} from 'bar'; +import { D, E } from 'bar'; +type T = C | F; + `, }, { // all type fix cases @@ -809,34 +809,34 @@ import { Type1, Type2 } from 'named_types'; import Type from 'default_type'; import * as Types from 'namespace_type'; import Default, { Named } from 'default_and_named_type'; -type T = Type1 | Type2 | Type | Types.A | Default | Named; - `, - output: ` -import type { Type1, Type2 } from 'named_types'; -import type Type from 'default_type'; -import type * as Types from 'namespace_type'; -import type { Named } from 'default_and_named_type'; -import type Default from 'default_and_named_type'; type T = Type1 | Type2 | Type | Types.A | Default | Named; `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, { - messageId: 'typeOverValue', line: 3, + messageId: 'typeOverValue', }, { - messageId: 'typeOverValue', line: 4, + messageId: 'typeOverValue', }, { - messageId: 'typeOverValue', line: 5, + messageId: 'typeOverValue', }, ], + output: ` +import type { Type1, Type2 } from 'named_types'; +import type Type from 'default_type'; +import type * as Types from 'namespace_type'; +import type { Named } from 'default_and_named_type'; +import type Default from 'default_and_named_type'; +type T = Type1 | Type2 | Type | Types.A | Default | Named; + `, }, { // some type fix cases @@ -845,42 +845,42 @@ import { Value1, Type1 } from 'named_import'; import Type2, { Value2 } from 'default_import'; import Value3, { Type3 } from 'default_import2'; import Type4, { Type5, Value4 } from 'default_and_named_import'; -type T = Type1 | Type2 | Type3 | Type4 | Type5; - `, - output: ` -import type { Type1 } from 'named_import'; -import { Value1 } from 'named_import'; -import type Type2 from 'default_import'; -import { Value2 } from 'default_import'; -import type { Type3 } from 'default_import2'; -import Value3 from 'default_import2'; -import type { Type5} from 'default_and_named_import'; -import type Type4 from 'default_and_named_import'; -import { Value4 } from 'default_and_named_import'; type T = Type1 | Type2 | Type3 | Type4 | Type5; `, errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"Type1"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"Type2"' }, line: 3, + messageId: 'someImportsAreOnlyTypes', }, { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"Type3"' }, line: 4, + messageId: 'someImportsAreOnlyTypes', }, { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"Type4" and "Type5"' }, line: 5, + messageId: 'someImportsAreOnlyTypes', }, ], + output: ` +import type { Type1 } from 'named_import'; +import { Value1 } from 'named_import'; +import type Type2 from 'default_import'; +import { Value2 } from 'default_import'; +import type { Type3 } from 'default_import2'; +import Value3 from 'default_import2'; +import type { Type5} from 'default_and_named_import'; +import type Type4 from 'default_and_named_import'; +import { Value4 } from 'default_and_named_import'; +type T = Type1 | Type2 | Type3 | Type4 | Type5; + `, }, // type annotations { @@ -888,64 +888,64 @@ type T = Type1 | Type2 | Type3 | Type4 | Type5; let foo: import('foo'); let bar: import('foo').Bar; `, - output: null, errors: [ { - messageId: 'noImportTypeAnnotations', line: 2, + messageId: 'noImportTypeAnnotations', }, { - messageId: 'noImportTypeAnnotations', line: 3, + messageId: 'noImportTypeAnnotations', }, ], + output: null, }, { code: ` let foo: import('foo'); `, - output: null, - options: [{ prefer: 'type-imports' }], errors: [ { - messageId: 'noImportTypeAnnotations', line: 2, + messageId: 'noImportTypeAnnotations', }, ], + options: [{ prefer: 'type-imports' }], + output: null, }, { code: ` import type Foo from 'foo'; -let foo: Foo; - `, - options: [{ prefer: 'no-type-imports' }], - output: ` -import Foo from 'foo'; let foo: Foo; `, errors: [ { - messageId: 'avoidImportType', line: 2, + messageId: 'avoidImportType', }, ], + options: [{ prefer: 'no-type-imports' }], + output: ` +import Foo from 'foo'; +let foo: Foo; + `, }, { code: ` import type { Foo } from 'foo'; -let foo: Foo; - `, - options: [{ prefer: 'no-type-imports' }], - output: ` -import { Foo } from 'foo'; let foo: Foo; `, errors: [ { - messageId: 'avoidImportType', line: 2, + messageId: 'avoidImportType', }, ], + options: [{ prefer: 'no-type-imports' }], + output: ` +import { Foo } from 'foo'; +let foo: Foo; + `, }, // type queries { @@ -955,18 +955,18 @@ import Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -975,18 +975,18 @@ import { Type } from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type { Type } from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -995,18 +995,18 @@ import * as Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type * as Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1015,6 +1015,12 @@ import type Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, + errors: [ + { + line: 2, + messageId: 'avoidImportType', + }, + ], options: [{ prefer: 'no-type-imports' }], output: ` import Type from 'foo'; @@ -1022,12 +1028,6 @@ import Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, - errors: [ - { - messageId: 'avoidImportType', - line: 2, - }, - ], }, { code: ` @@ -1036,6 +1036,12 @@ import type { Type } from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, + errors: [ + { + line: 2, + messageId: 'avoidImportType', + }, + ], options: [{ prefer: 'no-type-imports' }], output: ` import { Type } from 'foo'; @@ -1043,12 +1049,6 @@ import { Type } from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, - errors: [ - { - messageId: 'avoidImportType', - line: 2, - }, - ], }, { code: ` @@ -1057,6 +1057,12 @@ import type * as Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, + errors: [ + { + line: 2, + messageId: 'avoidImportType', + }, + ], options: [{ prefer: 'no-type-imports' }], output: ` import * as Type from 'foo'; @@ -1064,67 +1070,61 @@ import * as Type from 'foo'; type T = typeof Type; type T = typeof Type.foo; `, - errors: [ - { - messageId: 'avoidImportType', - line: 2, - }, - ], }, // exports { code: ` import Type from 'foo'; -export type { Type }; // is a type-only export - `, - output: ` -import type Type from 'foo'; - export type { Type }; // is a type-only export `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type Type from 'foo'; + +export type { Type }; // is a type-only export + `, }, { code: ` import { Type } from 'foo'; -export type { Type }; // is a type-only export - `, - output: ` -import type { Type } from 'foo'; - export type { Type }; // is a type-only export `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type { Type } from 'foo'; + +export type { Type }; // is a type-only export + `, }, { code: ` import * as Type from 'foo'; -export type { Type }; // is a type-only export - `, - output: ` -import type * as Type from 'foo'; - export type { Type }; // is a type-only export `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` +import type * as Type from 'foo'; + +export type { Type }; // is a type-only export + `, }, { code: ` @@ -1134,6 +1134,12 @@ export { Type }; // is a type-only export export default Type; // is a type-only export export type { Type }; // is a type-only export `, + errors: [ + { + line: 2, + messageId: 'avoidImportType', + }, + ], options: [{ prefer: 'no-type-imports' }], output: ` import Type from 'foo'; @@ -1142,12 +1148,6 @@ export { Type }; // is a type-only export export default Type; // is a type-only export export type { Type }; // is a type-only export `, - errors: [ - { - messageId: 'avoidImportType', - line: 2, - }, - ], }, { code: ` @@ -1157,6 +1157,12 @@ export { Type }; // is a type-only export export default Type; // is a type-only export export type { Type }; // is a type-only export `, + errors: [ + { + line: 2, + messageId: 'avoidImportType', + }, + ], options: [{ prefer: 'no-type-imports' }], output: ` import { Type } from 'foo'; @@ -1165,12 +1171,6 @@ export { Type }; // is a type-only export export default Type; // is a type-only export export type { Type }; // is a type-only export `, - errors: [ - { - messageId: 'avoidImportType', - line: 2, - }, - ], }, { code: ` @@ -1180,6 +1180,12 @@ export { Type }; // is a type-only export export default Type; // is a type-only export export type { Type }; // is a type-only export `, + errors: [ + { + line: 2, + messageId: 'avoidImportType', + }, + ], options: [{ prefer: 'no-type-imports' }], output: ` import * as Type from 'foo'; @@ -1188,12 +1194,6 @@ export { Type }; // is a type-only export export default Type; // is a type-only export export type { Type }; // is a type-only export `, - errors: [ - { - messageId: 'avoidImportType', - line: 2, - }, - ], }, { // type with comments @@ -1203,31 +1203,31 @@ import type // comment DefType from 'foo'; import type /*comment*/ { Type } from 'foo'; -type T = { a: AllType; b: DefType; c: Type }; - `, - options: [{ prefer: 'no-type-imports' }], - output: ` -import /*comment*/ * as AllType from 'foo'; -import // comment -DefType from 'foo'; -import /*comment*/ { Type } from 'foo'; - type T = { a: AllType; b: DefType; c: Type }; `, errors: [ { - messageId: 'avoidImportType', line: 2, + messageId: 'avoidImportType', }, { - messageId: 'avoidImportType', line: 3, + messageId: 'avoidImportType', }, { - messageId: 'avoidImportType', line: 5, + messageId: 'avoidImportType', }, ], + options: [{ prefer: 'no-type-imports' }], + output: ` +import /*comment*/ * as AllType from 'foo'; +import // comment +DefType from 'foo'; +import /*comment*/ { Type } from 'foo'; + +type T = { a: AllType; b: DefType; c: Type }; + `, }, { // https://github.com/typescript-eslint/typescript-eslint/issues/2775 @@ -1235,36 +1235,36 @@ type T = { a: AllType; b: DefType; c: Type }; import Default, * as Rest from 'module'; const a: Rest.A = ''; `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], options: [{ prefer: 'type-imports' }], output: ` import type * as Rest from 'module'; import Default from 'module'; const a: Rest.A = ''; `, - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { code: ` import Default, * as Rest from 'module'; const a: Default = ''; `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], options: [{ prefer: 'type-imports' }], output: ` import type Default from 'module'; import * as Rest from 'module'; const a: Default = ''; `, - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { code: ` @@ -1272,6 +1272,12 @@ import Default, * as Rest from 'module'; const a: Default = ''; const b: Rest.A = ''; `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], options: [{ prefer: 'type-imports' }], output: ` import type * as Rest from 'module'; @@ -1279,12 +1285,6 @@ import type Default from 'module'; const a: Default = ''; const b: Rest.A = ''; `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { // type with comments @@ -1292,18 +1292,18 @@ const b: Rest.A = ''; import Default, /*comment*/ * as Rest from 'module'; const a: Default = ''; `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], options: [{ prefer: 'type-imports' }], output: ` import type Default from 'module'; import /*comment*/ * as Rest from 'module'; const a: Default = ''; `, - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { // type with comments @@ -1311,18 +1311,18 @@ const a: Default = ''; import Default /*comment1*/, /*comment2*/ { Data } from 'module'; const a: Default = ''; `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], options: [{ prefer: 'type-imports' }], output: ` import type Default /*comment1*/ from 'module'; import /*comment2*/ { Data } from 'module'; const a: Default = ''; `, - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { code: ` @@ -1332,6 +1332,12 @@ class A { constructor(foo: Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; @deco @@ -1339,12 +1345,6 @@ class A { constructor(foo: Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1352,18 +1352,18 @@ import { type A, B } from 'foo'; type T = A; const b = B; `, - output: ` -import { A, B } from 'foo'; -type T = A; -const b = B; - `, - options: [{ prefer: 'no-type-imports' }], errors: [ { - messageId: 'avoidImportType', line: 2, + messageId: 'avoidImportType', }, ], + options: [{ prefer: 'no-type-imports' }], + output: ` +import { A, B } from 'foo'; +type T = A; +const b = B; + `, }, { code: ` @@ -1371,20 +1371,20 @@ import { A, B, type C } from 'foo'; type T = A | C; const b = B; `, - output: ` -import type { A} from 'foo'; -import { B, type C } from 'foo'; -type T = A | C; -const b = B; - `, - options: [{ prefer: 'type-imports' }], errors: [ { - messageId: 'someImportsAreOnlyTypes', data: { typeImports: '"A"' }, line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + options: [{ prefer: 'type-imports' }], + output: ` +import type { A} from 'foo'; +import { B, type C } from 'foo'; +type T = A | C; +const b = B; + `, }, // inline-type-imports @@ -1394,20 +1394,20 @@ import { A, B } from 'foo'; let foo: A; let bar: B; `, - output: ` -import { type A, type B } from 'foo'; -let foo: A; -let bar: B; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import { type A, type B } from 'foo'; +let foo: A; +let bar: B; + `, }, { code: ` @@ -1416,21 +1416,21 @@ import { A, B } from 'foo'; let foo: A; B(); `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], output: ` import { type A, B } from 'foo'; let foo: A; B(); `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { code: ` @@ -1438,20 +1438,20 @@ import { A, B } from 'foo'; type T = A; B(); `, - output: ` -import { type A, B } from 'foo'; -type T = A; -B(); - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'someImportsAreOnlyTypes', line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import { type A, B } from 'foo'; +type T = A; +B(); + `, }, { code: ` @@ -1460,25 +1460,25 @@ import { B } from 'foo'; type T = A; type U = B; `, - output: ` -import { type A } from 'foo'; -import { type B } from 'foo'; -type T = A; -type U = B; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, { - messageId: 'typeOverValue', line: 3, + messageId: 'typeOverValue', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import { type A } from 'foo'; +import { type B } from 'foo'; +type T = A; +type U = B; + `, }, { code: ` @@ -1487,25 +1487,25 @@ import B from 'foo'; type T = A; type U = B; `, - output: ` -import { type A } from 'foo'; -import type B from 'foo'; -type T = A; -type U = B; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, { - messageId: 'typeOverValue', line: 3, + messageId: 'typeOverValue', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import { type A } from 'foo'; +import type B from 'foo'; +type T = A; +type U = B; + `, }, { code: ` @@ -1514,21 +1514,21 @@ type T = B; type U = C; A(); `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], output: ` import A, { type B, type C } from 'foo'; type T = B; type U = C; A(); `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { code: ` @@ -1537,6 +1537,15 @@ type T = B; type U = C; type V = A; `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], output: ` import {type B, type C} from 'foo'; import type A from 'foo'; @@ -1544,15 +1553,6 @@ type T = B; type U = C; type V = A; `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1561,6 +1561,15 @@ type T = B; type U = D; type V = A; `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], output: ` import {type B, type C as D} from 'foo'; import type A from 'foo'; @@ -1568,53 +1577,44 @@ type T = B; type U = D; type V = A; `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` import { /* comment */ A, B } from 'foo'; type T = A; `, - output: ` -import { /* comment */ type A, B } from 'foo'; -type T = A; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'someImportsAreOnlyTypes', line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import { /* comment */ type A, B } from 'foo'; +type T = A; + `, }, { code: ` import { B, /* comment */ A } from 'foo'; type T = A; `, - output: ` -import { B, /* comment */ type A } from 'foo'; -type T = A; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'someImportsAreOnlyTypes', line: 2, + messageId: 'someImportsAreOnlyTypes', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import { B, /* comment */ type A } from 'foo'; +type T = A; + `, }, { code: ` @@ -1625,6 +1625,15 @@ const foo: A = B(); let bar: C; let baz: D; `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], output: ` import { type A, B, type C } from 'foo'; import type { D } from 'deez'; @@ -1633,15 +1642,6 @@ const foo: A = B(); let bar: C; let baz: D; `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { code: ` @@ -1651,6 +1651,15 @@ const foo: A = B(); let bar: C; let baz: D; `, + errors: [ + { + line: 2, + messageId: 'someImportsAreOnlyTypes', + }, + ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], output: ` import { type A, B, type C } from 'foo'; import type { D } from 'deez'; @@ -1658,53 +1667,44 @@ const foo: A = B(); let bar: C; let baz: D; `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], - errors: [ - { - messageId: 'someImportsAreOnlyTypes', - line: 2, - }, - ], }, { code: ` import A from 'foo'; export = {} as A; `, - output: ` -import type A from 'foo'; -export = {} as A; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import type A from 'foo'; +export = {} as A; + `, }, { code: ` import { A } from 'foo'; export = {} as A; `, - output: ` -import { type A } from 'foo'; -export = {} as A; - `, - options: [ - { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, - ], errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + options: [ + { fixStyle: 'inline-type-imports', prefer: 'type-imports' }, + ], + output: ` +import { type A } from 'foo'; +export = {} as A; + `, }, { code: ` @@ -1714,6 +1714,12 @@ export = {} as A; constructor(foo: Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; @deco @@ -1721,12 +1727,6 @@ export = {} as A; constructor(foo: Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1736,6 +1736,12 @@ export = {} as A; foo: Foo; } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; class A { @@ -1743,12 +1749,6 @@ export = {} as A; foo: Foo; } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1758,6 +1758,12 @@ export = {} as A; foo(foo: Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; class A { @@ -1765,12 +1771,6 @@ export = {} as A; foo(foo: Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1780,6 +1780,12 @@ export = {} as A; foo(): Foo {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; class A { @@ -1787,12 +1793,6 @@ export = {} as A; foo(): Foo {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1801,18 +1801,18 @@ export = {} as A; foo(@deco foo: Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; class A { foo(@deco foo: Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1822,6 +1822,12 @@ export = {} as A; set foo(value: Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; class A { @@ -1829,12 +1835,6 @@ export = {} as A; set foo(value: Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1846,6 +1846,12 @@ export = {} as A; set foo(value: Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; class A { @@ -1855,12 +1861,6 @@ export = {} as A; set foo(value: Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1872,6 +1872,12 @@ export = {} as A; set ['foo'](value: Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type Foo from 'foo'; class A { @@ -1881,12 +1887,6 @@ export = {} as A; set ['foo'](value: Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, { code: ` @@ -1896,6 +1896,12 @@ export = {} as A; constructor(foo: foo.Foo) {} } `, + errors: [ + { + line: 2, + messageId: 'typeOverValue', + }, + ], output: ` import type * as foo from 'foo'; @deco @@ -1903,12 +1909,6 @@ export = {} as A; constructor(foo: foo.Foo) {} } `, - errors: [ - { - messageId: 'typeOverValue', - line: 2, - }, - ], }, // https://github.com/typescript-eslint/typescript-eslint/issues/7209 { @@ -1917,15 +1917,15 @@ import 'foo'; import { Foo, Bar } from 'foo'; function test(foo: Foo) {} `, + errors: [ + { column: 1, line: 3, messageId: 'someImportsAreOnlyTypes' }, + ], output: ` import 'foo'; import type { Foo} from 'foo'; import { Bar } from 'foo'; function test(foo: Foo) {} `, - errors: [ - { messageId: 'someImportsAreOnlyTypes', line: 3, column: 1 }, - ], }, { code: ` @@ -1933,15 +1933,15 @@ import {} from 'foo'; import { Foo, Bar } from 'foo'; function test(foo: Foo) {} `, + errors: [ + { column: 1, line: 3, messageId: 'someImportsAreOnlyTypes' }, + ], output: ` import {} from 'foo'; import type { Foo} from 'foo'; import { Bar } from 'foo'; function test(foo: Foo) {} `, - errors: [ - { messageId: 'someImportsAreOnlyTypes', line: 3, column: 1 }, - ], }, ], }); @@ -1953,8 +1953,8 @@ describe('experimentalDecorators: true + emitDecoratorMetadata: true', () => { const ruleTester = new RuleTester({ languageOptions: { parserOptions: { - experimentalDecorators: true, emitDecoratorMetadata: true, + experimentalDecorators: true, }, }, }); @@ -2110,16 +2110,16 @@ describe('experimentalDecorators: true + emitDecoratorMetadata: true', () => { import Foo from 'foo'; export type T = Foo; `, - output: ` - import type Foo from 'foo'; - export type T = Foo; - `, errors: [ { - messageId: 'typeOverValue', line: 2, + messageId: 'typeOverValue', }, ], + output: ` + import type Foo from 'foo'; + export type T = Foo; + `, }, ], }); diff --git a/packages/eslint-plugin/tests/rules/default-param-last.test.ts b/packages/eslint-plugin/tests/rules/default-param-last.test.ts index bcad5e8a6d62..adda60c271b1 100644 --- a/packages/eslint-plugin/tests/rules/default-param-last.test.ts +++ b/packages/eslint-plugin/tests/rules/default-param-last.test.ts @@ -116,10 +116,10 @@ class Foo { code: 'function foo(a = 1, b: number) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -127,16 +127,16 @@ class Foo { code: 'function foo(a = 1, b = 2, c: number) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 21, endColumn: 26, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -144,16 +144,16 @@ class Foo { code: 'function foo(a = 1, b: number, c = 2, d: number) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 32, endColumn: 37, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -161,10 +161,10 @@ class Foo { code: 'function foo(a = 1, b: number, c = 2) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -172,10 +172,10 @@ class Foo { code: 'function foo(a = 1, b: number, ...c) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -183,10 +183,10 @@ class Foo { code: 'function foo(a?: number, b: number) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 24, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -194,10 +194,10 @@ class Foo { code: 'function foo(a: number, b?: number, c: number) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 25, endColumn: 35, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -205,16 +205,16 @@ class Foo { code: 'function foo(a = 1, b?: number, c: number) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 21, endColumn: 31, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -222,10 +222,10 @@ class Foo { code: 'function foo(a = 1, { b }) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -233,10 +233,10 @@ class Foo { code: 'function foo({ a } = {}, b) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 24, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -244,10 +244,10 @@ class Foo { code: 'function foo({ a, b } = { a: 1, b: 2 }, c) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 39, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -255,10 +255,10 @@ class Foo { code: 'function foo([a] = [], b) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 22, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -266,10 +266,10 @@ class Foo { code: 'function foo([a, b] = [1, 2], c) {}', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 29, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -277,10 +277,10 @@ class Foo { code: 'const foo = function (a = 1, b: number) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 28, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -288,16 +288,16 @@ class Foo { code: 'const foo = function (a = 1, b = 2, c: number) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 28, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 30, endColumn: 35, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -305,16 +305,16 @@ class Foo { code: 'const foo = function (a = 1, b: number, c = 2, d: number) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 28, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 41, endColumn: 46, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -322,10 +322,10 @@ class Foo { code: 'const foo = function (a = 1, b: number, c = 2) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 28, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -333,10 +333,10 @@ class Foo { code: 'const foo = function (a = 1, b: number, ...c) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 28, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -344,10 +344,10 @@ class Foo { code: 'const foo = function (a?: number, b: number) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 33, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -355,10 +355,10 @@ class Foo { code: 'const foo = function (a: number, b?: number, c: number) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 34, endColumn: 44, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -366,16 +366,16 @@ class Foo { code: 'const foo = function (a = 1, b?: number, c: number) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 28, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 30, endColumn: 40, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -383,10 +383,10 @@ class Foo { code: 'const foo = function (a = 1, { b }) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 28, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -394,10 +394,10 @@ class Foo { code: 'const foo = function ({ a } = {}, b) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 33, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -405,10 +405,10 @@ class Foo { code: 'const foo = function ({ a, b } = { a: 1, b: 2 }, c) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 48, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -416,10 +416,10 @@ class Foo { code: 'const foo = function ([a] = [], b) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 31, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -427,10 +427,10 @@ class Foo { code: 'const foo = function ([a, b] = [1, 2], c) {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 23, endColumn: 38, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -438,10 +438,10 @@ class Foo { code: 'const foo = (a = 1, b: number) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -449,16 +449,16 @@ class Foo { code: 'const foo = (a = 1, b = 2, c: number) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 21, endColumn: 26, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -466,16 +466,16 @@ class Foo { code: 'const foo = (a = 1, b: number, c = 2, d: number) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 32, endColumn: 37, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -483,10 +483,10 @@ class Foo { code: 'const foo = (a = 1, b: number, c = 2) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -494,10 +494,10 @@ class Foo { code: 'const foo = (a = 1, b: number, ...c) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -505,10 +505,10 @@ class Foo { code: 'const foo = (a?: number, b: number) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 24, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -516,10 +516,10 @@ class Foo { code: 'const foo = (a: number, b?: number, c: number) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 25, endColumn: 35, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -527,16 +527,16 @@ class Foo { code: 'const foo = (a = 1, b?: number, c: number) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, { - messageId: 'shouldBeLast', - line: 1, column: 21, endColumn: 31, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -544,10 +544,10 @@ class Foo { code: 'const foo = (a = 1, { b }) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 19, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -555,10 +555,10 @@ class Foo { code: 'const foo = ({ a } = {}, b) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 24, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -566,10 +566,10 @@ class Foo { code: 'const foo = ({ a, b } = { a: 1, b: 2 }, c) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 39, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -577,10 +577,10 @@ class Foo { code: 'const foo = ([a] = [], b) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 22, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -588,10 +588,10 @@ class Foo { code: 'const foo = ([a, b] = [1, 2], c) => {};', errors: [ { - messageId: 'shouldBeLast', - line: 1, column: 14, endColumn: 29, + line: 1, + messageId: 'shouldBeLast', }, ], }, @@ -607,10 +607,10 @@ class Foo { `, errors: [ { - messageId: 'shouldBeLast', - line: 5, column: 5, endColumn: 25, + line: 5, + messageId: 'shouldBeLast', }, ], }, @@ -626,10 +626,10 @@ class Foo { `, errors: [ { - messageId: 'shouldBeLast', - line: 5, column: 5, endColumn: 20, + line: 5, + messageId: 'shouldBeLast', }, ], }, @@ -644,10 +644,10 @@ class Foo { `, errors: [ { - messageId: 'shouldBeLast', - line: 4, column: 5, endColumn: 22, + line: 4, + messageId: 'shouldBeLast', }, ], }, @@ -662,10 +662,10 @@ class Foo { `, errors: [ { - messageId: 'shouldBeLast', - line: 4, column: 5, endColumn: 17, + line: 4, + messageId: 'shouldBeLast', }, ], }, @@ -677,10 +677,10 @@ class Foo { `, errors: [ { - messageId: 'shouldBeLast', - line: 3, column: 15, endColumn: 20, + line: 3, + messageId: 'shouldBeLast', }, ], }, @@ -692,10 +692,10 @@ class Foo { `, errors: [ { - messageId: 'shouldBeLast', - line: 3, column: 15, endColumn: 25, + line: 3, + messageId: 'shouldBeLast', }, ], }, diff --git a/packages/eslint-plugin/tests/rules/dot-notation.test.ts b/packages/eslint-plugin/tests/rules/dot-notation.test.ts index 580ddef5715c..9355a46750af 100644 --- a/packages/eslint-plugin/tests/rules/dot-notation.test.ts +++ b/packages/eslint-plugin/tests/rules/dot-notation.test.ts @@ -8,8 +8,8 @@ const rootPath = getFixturesRootDir(); const ruleTester = new RuleTester({ languageOptions: { parserOptions: { - tsconfigRootDir: rootPath, project: './tsconfig.json', + tsconfigRootDir: rootPath, }, }, }); @@ -57,8 +57,8 @@ ruleTester.run('dot-notation', rule, { }, { code: 'a[`while`];', - options: [{ allowKeywords: false }], languageOptions: { parserOptions: { ecmaVersion: 6 } }, + options: [{ allowKeywords: false }], }, { code: 'a[`time range`];', @@ -125,8 +125,8 @@ let dingus: Dingus | undefined; dingus?.nested.property; dingus?.nested['hello']; `, - options: [{ allowIndexSignaturePropertyAccess: true }], languageOptions: { parserOptions: { ecmaVersion: 2020 } }, + options: [{ allowIndexSignaturePropertyAccess: true }], }, { code: ` @@ -161,6 +161,7 @@ class X { const x = new X(); x['priv_prop'] = 123; `, + errors: [{ messageId: 'useDot' }], options: [{ allowPrivateClassPropertyAccess: false }], output: ` class X { @@ -170,7 +171,6 @@ class X { const x = new X(); x.priv_prop = 123; `, - errors: [{ messageId: 'useDot' }], }, { code: ` @@ -181,6 +181,7 @@ class X { const x = new X(); x['pub_prop'] = 123; `, + errors: [{ messageId: 'useDot' }], output: ` class X { public pub_prop = 123; @@ -189,7 +190,6 @@ class X { const x = new X(); x.pub_prop = 123; `, - errors: [{ messageId: 'useDot' }], }, // baseRule @@ -201,69 +201,69 @@ x.pub_prop = 123; // }, { code: "a['true'];", + errors: [{ data: { key: q('true') }, messageId: 'useDot' }], output: 'a.true;', - errors: [{ messageId: 'useDot', data: { key: q('true') } }], }, { code: "a['time'];", - output: 'a.time;', + errors: [{ data: { key: '"time"' }, messageId: 'useDot' }], languageOptions: { parserOptions: { ecmaVersion: 6 } }, - errors: [{ messageId: 'useDot', data: { key: '"time"' } }], + output: 'a.time;', }, { code: 'a[null];', + errors: [{ data: { key: 'null' }, messageId: 'useDot' }], output: 'a.null;', - errors: [{ messageId: 'useDot', data: { key: 'null' } }], }, { code: 'a[true];', + errors: [{ data: { key: 'true' }, messageId: 'useDot' }], output: 'a.true;', - errors: [{ messageId: 'useDot', data: { key: 'true' } }], }, { code: 'a[false];', + errors: [{ data: { key: 'false' }, messageId: 'useDot' }], output: 'a.false;', - errors: [{ messageId: 'useDot', data: { key: 'false' } }], }, { code: "a['b'];", + errors: [{ data: { key: q('b') }, messageId: 'useDot' }], output: 'a.b;', - errors: [{ messageId: 'useDot', data: { key: q('b') } }], }, { code: "a.b['c'];", + errors: [{ data: { key: q('c') }, messageId: 'useDot' }], output: 'a.b.c;', - errors: [{ messageId: 'useDot', data: { key: q('c') } }], }, { code: "a['_dangle'];", - output: 'a._dangle;', + errors: [{ data: { key: q('_dangle') }, messageId: 'useDot' }], options: [{ allowPattern: '^[a-z]+(_[a-z]+)+$' }], - errors: [{ messageId: 'useDot', data: { key: q('_dangle') } }], + output: 'a._dangle;', }, { code: "a['SHOUT_CASE'];", - output: 'a.SHOUT_CASE;', + errors: [{ data: { key: q('SHOUT_CASE') }, messageId: 'useDot' }], options: [{ allowPattern: '^[a-z]+(_[a-z]+)+$' }], - errors: [{ messageId: 'useDot', data: { key: q('SHOUT_CASE') } }], + output: 'a.SHOUT_CASE;', }, { code: noFormat` a ['SHOUT_CASE']; - `, - output: ` -a - .SHOUT_CASE; `, errors: [ { - messageId: 'useDot', + column: 4, data: { key: q('SHOUT_CASE') }, line: 3, - column: 4, + messageId: 'useDot', }, ], + output: ` +a + .SHOUT_CASE; + `, }, { code: @@ -272,75 +272,75 @@ a ' ["catch"](function(){})\n' + ' .then(function(){})\n' + ' ["catch"](function(){});', - output: - 'getResource()\n' + - ' .then(function(){})\n' + - ' .catch(function(){})\n' + - ' .then(function(){})\n' + - ' .catch(function(){});', errors: [ { - messageId: 'useDot', + column: 6, data: { key: q('catch') }, line: 3, - column: 6, + messageId: 'useDot', }, { - messageId: 'useDot', + column: 6, data: { key: q('catch') }, line: 5, - column: 6, + messageId: 'useDot', }, ], + output: + 'getResource()\n' + + ' .then(function(){})\n' + + ' .catch(function(){})\n' + + ' .then(function(){})\n' + + ' .catch(function(){});', }, { code: noFormat` foo .while; `, + errors: [{ data: { key: 'while' }, messageId: 'useBrackets' }], + options: [{ allowKeywords: false }], output: ` foo ["while"]; `, - options: [{ allowKeywords: false }], - errors: [{ messageId: 'useBrackets', data: { key: 'while' } }], }, { code: "foo[/* comment */ 'bar'];", + errors: [{ data: { key: q('bar') }, messageId: 'useDot' }], output: null, // Not fixed due to comment - errors: [{ messageId: 'useDot', data: { key: q('bar') } }], }, { code: "foo['bar' /* comment */];", + errors: [{ data: { key: q('bar') }, messageId: 'useDot' }], output: null, // Not fixed due to comment - errors: [{ messageId: 'useDot', data: { key: q('bar') } }], }, { code: "foo['bar'];", + errors: [{ data: { key: q('bar') }, messageId: 'useDot' }], output: 'foo.bar;', - errors: [{ messageId: 'useDot', data: { key: q('bar') } }], }, { code: 'foo./* comment */ while;', - output: null, // Not fixed due to comment + errors: [{ data: { key: 'while' }, messageId: 'useBrackets' }], options: [{ allowKeywords: false }], - errors: [{ messageId: 'useBrackets', data: { key: 'while' } }], + output: null, // Not fixed due to comment }, { code: 'foo[null];', + errors: [{ data: { key: 'null' }, messageId: 'useDot' }], output: 'foo.null;', - errors: [{ messageId: 'useDot', data: { key: 'null' } }], }, { code: "foo['bar'] instanceof baz;", + errors: [{ data: { key: q('bar') }, messageId: 'useDot' }], output: 'foo.bar instanceof baz;', - errors: [{ messageId: 'useDot', data: { key: q('bar') } }], }, { code: 'let.if();', - output: null, // `let["if"]()` is a syntax error because `let[` indicates a destructuring variable declaration + errors: [{ data: { key: 'if' }, messageId: 'useBrackets' }], options: [{ allowKeywords: false }], - errors: [{ messageId: 'useBrackets', data: { key: 'if' } }], + output: null, // `let["if"]()` is a syntax error because `let[` indicates a destructuring variable declaration }, { code: ` @@ -351,6 +351,7 @@ class X { const x = new X(); x['protected_prop'] = 123; `, + errors: [{ messageId: 'useDot' }], options: [{ allowProtectedClassPropertyAccess: false }], output: ` class X { @@ -360,7 +361,6 @@ class X { const x = new X(); x.protected_prop = 123; `, - errors: [{ messageId: 'useDot' }], }, { code: ` @@ -372,8 +372,8 @@ class X { const x = new X(); x['prop'] = 'hello'; `, - options: [{ allowIndexSignaturePropertyAccess: true }], errors: [{ messageId: 'useDot' }], + options: [{ allowIndexSignaturePropertyAccess: true }], output: ` class X { prop: string; diff --git a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts index ae80f009727b..52eafebb8e04 100644 --- a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts @@ -182,7 +182,6 @@ class App { // https://github.com/typescript-eslint/typescript-eslint/issues/7552 { code: 'const foo =