diff --git a/.eslintrc.js b/.eslintrc.js index d852a75216f9..400ed10c4aaa 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -53,7 +53,6 @@ module.exports = { 'deprecation/deprecation': 'error', // TODO(#7338): Investigate enabling these soon ✨ - '@typescript-eslint/no-unnecessary-condition': 'off', '@typescript-eslint/no-dynamic-delete': 'off', '@typescript-eslint/prefer-nullish-coalescing': 'off', @@ -85,6 +84,11 @@ module.exports = { ], '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-non-null-assertion': 'off', + 'no-constant-condition': 'off', + '@typescript-eslint/no-unnecessary-condition': [ + 'error', + { allowConstantLoopConditions: true }, + ], '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/prefer-literal-enum-member': [ 'error', diff --git a/packages/ast-spec/tests/util/serializers/Node.ts b/packages/ast-spec/tests/util/serializers/Node.ts index bd6a9536ba76..a0fe170f4501 100644 --- a/packages/ast-spec/tests/util/serializers/Node.ts +++ b/packages/ast-spec/tests/util/serializers/Node.ts @@ -3,7 +3,9 @@ import type { NewPlugin } from 'pretty-format'; import type * as TSESTree from '../../../src'; import { AST_NODE_TYPES } from '../../../src'; -function sortKeys(node: TSESTree.Node): (keyof typeof node)[] { +function sortKeys( + node: Node, +): (keyof typeof node)[] { const keySet = new Set(Object.keys(node)); // type place as first key @@ -39,7 +41,14 @@ const serializer: NewPlugin = { test(val: unknown) { return isObject(val) && hasValidType(val.type); }, - serialize(node: TSESTree.Node, config, indentation, depth, refs, printer) { + serialize( + node: TSESTree.Node & Record, + config, + indentation, + depth, + refs, + printer, + ) { const keys = sortKeys(node); const type = node.type; const loc = node.loc; diff --git a/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts b/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts index 7656fa4ccf5c..4c13b42b9cc5 100644 --- a/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts +++ b/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts @@ -172,6 +172,7 @@ export default createRule({ } catch (ex) { // ex instanceof Error is false as of @prettier/sync@0.3.0, as is ex instanceof SyntaxError if ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition (ex as Partial | undefined)?.constructor?.name !== 'SyntaxError' ) { diff --git a/packages/eslint-plugin/src/rules/consistent-type-exports.ts b/packages/eslint-plugin/src/rules/consistent-type-exports.ts index dc0f410f681b..127dde8831df 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-exports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-exports.ts @@ -109,6 +109,7 @@ export default createRule({ ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration): void { // Coerce the source into a string for use as a lookup entry. const source = getSourceFromExport(node) ?? 'undefined'; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const sourceExports = (sourceExportsMap[source] ||= { source, reportValueExports: [], diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts index ee69229e766c..fd452d750b66 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts @@ -119,6 +119,7 @@ export default createRule({ ImportDeclaration(node): void { const source = node.source.value; // sourceImports is the object containing all the specifics for a particular import source, type or value + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition sourceImportsMap[source] ??= { source, reportValueImports: [], // if there is a mismatch where type importKind but value specifiers @@ -194,8 +195,9 @@ export default createRule({ } } if (ref.isValueReference) { - let parent: TSESTree.Node | undefined = - ref.identifier.parent; + let parent = ref.identifier.parent as + | TSESTree.Node + | undefined; let child: TSESTree.Node = ref.identifier; while (parent) { switch (parent.type) { diff --git a/packages/eslint-plugin/src/rules/no-extra-parens.ts b/packages/eslint-plugin/src/rules/no-extra-parens.ts index c938dfe4896d..279ea3840473 100644 --- a/packages/eslint-plugin/src/rules/no-extra-parens.ts +++ b/packages/eslint-plugin/src/rules/no-extra-parens.ts @@ -277,6 +277,7 @@ export default createRule({ } }, }; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (rules.ForInStatement && rules.ForOfStatement) { overrides.ForInStatement = function (node): void { if (isTypeAssertion(node.right)) { diff --git a/packages/eslint-plugin/src/rules/no-shadow.ts b/packages/eslint-plugin/src/rules/no-shadow.ts index b97971339617..c1e637962a5f 100644 --- a/packages/eslint-plugin/src/rules/no-shadow.ts +++ b/packages/eslint-plugin/src/rules/no-shadow.ts @@ -412,7 +412,7 @@ export default createRule({ return false; } - let node: TSESTree.Node | undefined = outerDef.name; + let node = outerDef.name as TSESTree.Node | undefined; const location = callExpression.range[1]; while (node) { diff --git a/packages/eslint-plugin/src/rules/prefer-readonly.ts b/packages/eslint-plugin/src/rules/prefer-readonly.ts index d34a57b47679..be92e541781c 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly.ts @@ -105,7 +105,7 @@ export default createRule({ function isDestructuringAssignment( node: ts.PropertyAccessExpression, ): boolean { - let current: ts.Node = node.parent; + let current = node.parent as ts.Node | undefined; while (current) { const parent = current.parent; diff --git a/packages/eslint-plugin/src/rules/return-await.ts b/packages/eslint-plugin/src/rules/return-await.ts index 4cc53535378d..fa3c6d5479e0 100644 --- a/packages/eslint-plugin/src/rules/return-await.ts +++ b/packages/eslint-plugin/src/rules/return-await.ts @@ -71,7 +71,7 @@ export default createRule({ } function inTry(node: ts.Node): boolean { - let ancestor = node.parent; + let ancestor = node.parent as ts.Node | undefined; while (ancestor && !ts.isFunctionLike(ancestor)) { if (ts.isTryStatement(ancestor)) { @@ -85,7 +85,7 @@ export default createRule({ } function inCatch(node: ts.Node): boolean { - let ancestor = node.parent; + let ancestor = node.parent as ts.Node | undefined; while (ancestor && !ts.isFunctionLike(ancestor)) { if (ts.isCatchClause(ancestor)) { @@ -99,7 +99,7 @@ export default createRule({ } function isReturnPromiseInFinally(node: ts.Node): boolean { - let ancestor = node.parent; + let ancestor = node.parent as ts.Node | undefined; while (ancestor && !ts.isFunctionLike(ancestor)) { if ( @@ -116,7 +116,7 @@ export default createRule({ } function hasFinallyBlock(node: ts.Node): boolean { - let ancestor = node.parent; + let ancestor = node.parent as ts.Node | undefined; while (ancestor && !ts.isFunctionLike(ancestor)) { if (ts.isTryStatement(ancestor)) { diff --git a/packages/eslint-plugin/src/rules/unified-signatures.ts b/packages/eslint-plugin/src/rules/unified-signatures.ts index e6237dc8667d..0d4b8e8035d3 100644 --- a/packages/eslint-plugin/src/rules/unified-signatures.ts +++ b/packages/eslint-plugin/src/rules/unified-signatures.ts @@ -172,12 +172,12 @@ export default createRule({ const isTypeParameter = getIsTypeParameter(typeParameters); for (const overloads of signatures) { forEachPair(overloads, (a, b) => { - const signature0 = (a as MethodDefinition).value ?? a; - const signature1 = (b as MethodDefinition).value ?? b; + const signature0 = (a as Partial).value ?? a; + const signature1 = (b as Partial).value ?? b; const unify = compareSignatures( - signature0, - signature1, + signature0 as SignatureDefinition, + signature1 as SignatureDefinition, isTypeParameter, ); if (unify !== undefined) { diff --git a/packages/eslint-plugin/src/util/getThisExpression.ts b/packages/eslint-plugin/src/util/getThisExpression.ts index 7a5bcd6ebfd5..196254c35f96 100644 --- a/packages/eslint-plugin/src/util/getThisExpression.ts +++ b/packages/eslint-plugin/src/util/getThisExpression.ts @@ -4,7 +4,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; export function getThisExpression( node: TSESTree.Node, ): TSESTree.ThisExpression | undefined { - while (node) { + while (true) { if (node.type === AST_NODE_TYPES.CallExpression) { node = node.callee; } else if (node.type === AST_NODE_TYPES.ThisExpression) { diff --git a/packages/rule-tester/src/RuleTester.ts b/packages/rule-tester/src/RuleTester.ts index 181372318120..ad28d93a1478 100644 --- a/packages/rule-tester/src/RuleTester.ts +++ b/packages/rule-tester/src/RuleTester.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ // Forked from https://github.com/eslint/eslint/blob/ad9dd6a933fd098a0d99c6a9aa059850535c23ee/lib/rule-tester/rule-tester.js import assert from 'node:assert'; diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index 38e4a0d2adac..f5d36f2fa0ba 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -88,6 +88,7 @@ class Referencer extends Visitor { private populateGlobalsFromLib(globalScope: GlobalScope): void { for (const lib of this.#lib) { const variables = TSLibraries[lib]; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition /* istanbul ignore if */ if (!variables) { throw new Error(`Invalid value for lib provided: ${lib}`); } diff --git a/packages/scope-manager/src/referencer/TypeVisitor.ts b/packages/scope-manager/src/referencer/TypeVisitor.ts index 2bb922be4023..786b1059b5a1 100644 --- a/packages/scope-manager/src/referencer/TypeVisitor.ts +++ b/packages/scope-manager/src/referencer/TypeVisitor.ts @@ -53,6 +53,7 @@ class TypeVisitor extends Visitor { }); // there are a few special cases where the type annotation is owned by the parameter, not the pattern + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!didVisitAnnotation && 'typeAnnotation' in param) { this.visit(param.typeAnnotation); } diff --git a/packages/type-utils/src/getTypeArguments.ts b/packages/type-utils/src/getTypeArguments.ts index 06b85cead688..4189cd20eec2 100644 --- a/packages/type-utils/src/getTypeArguments.ts +++ b/packages/type-utils/src/getTypeArguments.ts @@ -8,6 +8,7 @@ export function getTypeArguments( checker: ts.TypeChecker, ): readonly ts.Type[] { // getTypeArguments was only added in TS3.7 + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (checker.getTypeArguments) { return checker.getTypeArguments(type); } diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 4be5c8783b6d..5dcde889e8dd 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ // There's lots of funny stuff due to the typing of ts.Node /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access */ import * as ts from 'typescript'; diff --git a/packages/typescript-estree/src/create-program/shared.ts b/packages/typescript-estree/src/create-program/shared.ts index 8966093372ed..84784797051a 100644 --- a/packages/typescript-estree/src/create-program/shared.ts +++ b/packages/typescript-estree/src/create-program/shared.ts @@ -55,6 +55,7 @@ type CanonicalPath = string & { __brand: unknown }; // typescript doesn't provide a ts.sys implementation for browser environments const useCaseSensitiveFileNames = + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition ts.sys !== undefined ? ts.sys.useCaseSensitiveFileNames : true; const correctPathCasing = useCaseSensitiveFileNames ? (filePath: string): string => filePath @@ -118,6 +119,7 @@ function getAstFromProgram( */ function createHash(content: string): string { // No ts.sys in browser environments. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (ts.sys?.createHash) { return ts.sys.createHash(content); } diff --git a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts index c2b67e795750..6c6e770bd20c 100644 --- a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts +++ b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts @@ -56,6 +56,7 @@ function createProgramFromConfigFile( configFile: string, projectDirectory?: string, ): ts.Program { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (ts.sys === undefined) { throw new Error( '`createProgramFromConfigFile` is only supported in a Node-like environment.', diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 219f850aa03d..a131e8043e34 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -430,7 +430,7 @@ export function findFirstMatchingAncestor( if (predicate(current)) { return current; } - current = current.parent; + current = current.parent as ts.Node | undefined; } return undefined; } diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 50c39898f491..6b3429571014 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -25,12 +25,15 @@ let TSSERVER_PROJECT_SERVICE: TypeScriptProjectService | null = null; // NOTE - we intentionally use "unnecessary" `?.` here because in TS<5.3 this enum doesn't exist // This object exists so we can centralize these for tracking and so we don't proliferate these across the file +// https://github.com/microsoft/TypeScript/issues/56579 +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ const JSDocParsingMode = { ParseAll: ts.JSDocParsingMode?.ParseAll, ParseNone: ts.JSDocParsingMode?.ParseNone, ParseForTypeErrors: ts.JSDocParsingMode?.ParseForTypeErrors, ParseForTypeInfo: ts.JSDocParsingMode?.ParseForTypeInfo, } as const; +/* eslint-enable @typescript-eslint/no-unnecessary-condition */ export function createParseSettings( code: ts.SourceFile | string, diff --git a/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts b/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts index f860ae6f1004..422499683435 100644 --- a/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts +++ b/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts @@ -33,6 +33,8 @@ export function warnAboutTSVersion( if ( passedLoggerFn || + // See https://github.com/typescript-eslint/typescript-eslint/issues/7896 + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition (typeof process === 'undefined' ? false : process.stdout?.isTTY) ) { const border = '============='; diff --git a/packages/utils/src/eslint-utils/context.ts b/packages/utils/src/eslint-utils/context.ts index 93e8283a7b61..28ddb3cc154e 100644 --- a/packages/utils/src/eslint-utils/context.ts +++ b/packages/utils/src/eslint-utils/context.ts @@ -1,6 +1,6 @@ // Wrappers around ESLint's deprecation of existing methods // We'll be able to drop them once we no longer support ESLint <8.40.0. -/* eslint-disable deprecation/deprecation */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition, deprecation/deprecation */ import type { Scope, SourceCode } from '../ts-eslint'; import type { RuleContext } from '../ts-eslint/Rule'; import type { TSESTree } from '../ts-estree'; diff --git a/packages/utils/src/eslint-utils/getParserServices.ts b/packages/utils/src/eslint-utils/getParserServices.ts index 6d36e26b4db9..485860036c63 100644 --- a/packages/utils/src/eslint-utils/getParserServices.ts +++ b/packages/utils/src/eslint-utils/getParserServices.ts @@ -69,7 +69,7 @@ function getParserServices( if ( // eslint-disable-next-line deprecation/deprecation -- TODO - support for ESLint v9 with backwards-compatible support for ESLint v8 context.parserServices?.esTreeNodeToTSNodeMap == null || - // eslint-disable-next-line deprecation/deprecation -- TODO - support for ESLint v9 with backwards-compatible support for ESLint v8 + // eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-unnecessary-condition -- TODO - support for ESLint v9 with backwards-compatible support for ESLint v8 context.parserServices.tsNodeToESTreeNodeMap == null ) { throw new Error(ERROR_MESSAGE); diff --git a/packages/website/src/components/Playground.tsx b/packages/website/src/components/Playground.tsx index 1f89c699ec68..135684d143c2 100644 --- a/packages/website/src/components/Playground.tsx +++ b/packages/website/src/components/Playground.tsx @@ -1,3 +1,5 @@ +// See https://github.com/typescript-eslint/typescript-eslint/issues/7630. +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ import { useWindowSize } from '@docusaurus/theme-common'; import clsx from 'clsx'; import type * as ESQuery from 'esquery'; diff --git a/packages/website/src/components/ast/ASTViewer.tsx b/packages/website/src/components/ast/ASTViewer.tsx index 087464a56f1f..f8e87937bc0d 100644 --- a/packages/website/src/components/ast/ASTViewer.tsx +++ b/packages/website/src/components/ast/ASTViewer.tsx @@ -21,6 +21,7 @@ export interface ASTViewerProps { function tryToApplyFilter(value: T, filter?: ESQuery.Selector): T | T[] { try { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (window.esquery && filter) { // @ts-expect-error - esquery requires js ast types return window.esquery.match(value, filter); diff --git a/packages/website/src/components/ast/selectedRange.ts b/packages/website/src/components/ast/selectedRange.ts index 4fa89b843a52..a92da6a801b0 100644 --- a/packages/website/src/components/ast/selectedRange.ts +++ b/packages/website/src/components/ast/selectedRange.ts @@ -32,7 +32,7 @@ function findInObject( visited: Set, ): { key: string[]; - value: object; + value: unknown; } | null { const children = Object.entries(iter); for (const [name, child] of children) { @@ -73,7 +73,7 @@ export function findSelectionPath( ): { path: string[]; node: object | null } { const nodePath = ['ast']; const visited = new Set(); - let currentNode: object | null = node; + let currentNode: unknown = node; while (currentNode) { // infinite loop guard if (visited.has(currentNode)) { diff --git a/packages/website/src/components/config/ConfigTypeScript.tsx b/packages/website/src/components/config/ConfigTypeScript.tsx index b367af6fafb7..fb41fafc1976 100644 --- a/packages/website/src/components/config/ConfigTypeScript.tsx +++ b/packages/website/src/components/config/ConfigTypeScript.tsx @@ -35,6 +35,7 @@ function ConfigTypeScript(props: ConfigTypeScriptProps): React.JSX.Element { getTypescriptOptions().reduce>( (group, item) => { const category = item.category!.message; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition group[category] ??= { heading: category, fields: [], diff --git a/packages/website/src/components/linter/config.ts b/packages/website/src/components/linter/config.ts index 4ae0a5987b6f..8262487369c1 100644 --- a/packages/website/src/components/linter/config.ts +++ b/packages/website/src/components/linter/config.ts @@ -17,6 +17,8 @@ export const defaultParseSettings: ParseSettings = { EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, extraFileExtensions: [], filePath: '', + // JSDocParsingMode was added in TS 5.3. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition jsDocParsingMode: window.ts?.JSDocParsingMode?.ParseAll, jsx: true, loc: true, diff --git a/packages/website/src/components/linter/createLinter.ts b/packages/website/src/components/linter/createLinter.ts index 7f3f8541e787..3e015053d136 100644 --- a/packages/website/src/components/linter/createLinter.ts +++ b/packages/website/src/components/linter/createLinter.ts @@ -76,29 +76,27 @@ export function createLinter( const triggerLint = (filename: string): void => { console.info('[Editor] linting triggered for file', filename); const code = system.readFile(filename) ?? '\n'; - if (code != null) { - try { - const messages = linter.verify(code, eslintConfig, filename); - onLint.trigger(filename, messages); - } catch (e) { - const lintMessage: Linter.LintMessage = { - source: 'eslint', - ruleId: '', - severity: 2, - nodeType: '', - column: 1, - line: 1, - message: String(e instanceof Error ? e.stack : e), - }; - if (typeof e === 'object' && e && 'currentNode' in e) { - const node = e.currentNode as TSESTree.Node; - lintMessage.column = node.loc.start.column + 1; - lintMessage.line = node.loc.start.line; - lintMessage.endColumn = node.loc.end.column + 1; - lintMessage.endLine = node.loc.end.line; - } - onLint.trigger(filename, [lintMessage]); + try { + const messages = linter.verify(code, eslintConfig, filename); + onLint.trigger(filename, messages); + } catch (e) { + const lintMessage: Linter.LintMessage = { + source: 'eslint', + ruleId: '', + severity: 2, + nodeType: '', + column: 1, + line: 1, + message: String(e instanceof Error ? e.stack : e), + }; + if (typeof e === 'object' && e && 'currentNode' in e) { + const node = e.currentNode as TSESTree.Node; + lintMessage.column = node.loc.start.column + 1; + lintMessage.line = node.loc.start.line; + lintMessage.endColumn = node.loc.end.column + 1; + lintMessage.endLine = node.loc.end.line; } + onLint.trigger(filename, [lintMessage]); } }; diff --git a/packages/website/src/components/linter/utils.ts b/packages/website/src/components/linter/utils.ts index 6546aa697a74..4d01c8708dd5 100644 --- a/packages/website/src/components/linter/utils.ts +++ b/packages/website/src/components/linter/utils.ts @@ -92,6 +92,7 @@ export function parseMarkers( ? 'TypeScript' : marker.owner; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!result[group]) { result[group] = { group: group, diff --git a/packages/website/src/components/typeDetails/TypesDetails.tsx b/packages/website/src/components/typeDetails/TypesDetails.tsx index 9155918e5b50..fb5147413f68 100644 --- a/packages/website/src/components/typeDetails/TypesDetails.tsx +++ b/packages/website/src/components/typeDetails/TypesDetails.tsx @@ -51,17 +51,15 @@ export function TypesDetails({ - {selectedNode && ( - -
- -
-
- )} + +
+ +
+
); }