diff --git a/eslint.config.mjs b/eslint.config.mjs index 84460be0a90e..3e5dca7ea2f3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -132,7 +132,6 @@ export default tseslint.config( { allowIIFEs: true }, ], '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-non-null-assertion': 'off', 'no-constant-condition': 'off', '@typescript-eslint/no-unnecessary-condition': [ 'error', @@ -343,6 +342,7 @@ export default tseslint.config( 'error', { allow: ['arrowFunctions'] }, ], + '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', 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 19cb2d33fb1c..6c0f716d79e9 100644 --- a/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts +++ b/packages/eslint-plugin-internal/src/rules/plugin-test-formatting.ts @@ -55,6 +55,7 @@ function getExpectedIndentForNode( sourceCodeLines: string[], ): number { const lineIdx = node.loc.start.line - 1; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const indent = START_OF_LINE_WHITESPACE_MATCHER.exec( sourceCodeLines[lineIdx], )![1]; @@ -348,6 +349,7 @@ export default createRule({ // +2 because we expect the string contents are indented one level const expectedIndent = parentIndent + 2; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const firstLineIndent = START_OF_LINE_WHITESPACE_MATCHER.exec( lines[0], )![1]; @@ -370,6 +372,7 @@ export default createRule({ continue; } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const matches = START_OF_LINE_WHITESPACE_MATCHER.exec(line)!; const indent = matches[1]; diff --git a/packages/eslint-plugin/src/rules/ban-ts-comment.ts b/packages/eslint-plugin/src/rules/ban-ts-comment.ts index 40e6acbbd6d7..5a5aeef35273 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-comment.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-comment.ts @@ -131,6 +131,7 @@ export default createRule<[Options], MessageIds>({ if (!match) { return; } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { directive, description } = match.groups!; const fullDirective = `ts-${directive}` as keyof Options; diff --git a/packages/eslint-plugin/src/rules/block-spacing.ts b/packages/eslint-plugin/src/rules/block-spacing.ts index e127bb1a6a02..05db7c76199c 100644 --- a/packages/eslint-plugin/src/rules/block-spacing.ts +++ b/packages/eslint-plugin/src/rules/block-spacing.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; diff --git a/packages/eslint-plugin/src/rules/brace-style.ts b/packages/eslint-plugin/src/rules/brace-style.ts index ed76480e75f7..1021c9c9a48c 100644 --- a/packages/eslint-plugin/src/rules/brace-style.ts +++ b/packages/eslint-plugin/src/rules/brace-style.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import type { diff --git a/packages/eslint-plugin/src/rules/comma-spacing.ts b/packages/eslint-plugin/src/rules/comma-spacing.ts index e22bbaa73f45..cd283e4c97ac 100644 --- a/packages/eslint-plugin/src/rules/comma-spacing.ts +++ b/packages/eslint-plugin/src/rules/comma-spacing.ts @@ -71,6 +71,7 @@ export default createRule({ for (const element of node.elements) { let token: TSESTree.Token | null; if (element == null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion token = context.sourceCode.getTokenAfter(previousToken!); if (token && isCommaToken(token)) { ignoredTokens.add(token); diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 5ef447ede85b..166a2a8ecfda 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -1,7 +1,7 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule } from '../util'; +import { createRule, nullThrows, NullThrowsReasons } from '../util'; type MessageIds = 'preferConstructor' | 'preferTypeAnnotation'; type Options = ['constructor' | 'type-annotation']; @@ -94,7 +94,10 @@ export default createRule({ } // If the property's computed, we have to attach the // annotation after the square bracket, not the enclosed expression - return context.sourceCode.getTokenAfter(node.key)!; + return nullThrows( + context.sourceCode.getTokenAfter(node.key), + NullThrowsReasons.MissingToken(']', 'key'), + ); } return [ fixer.remove(typeArguments), diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 418cfa48f928..e3d88fb69c7c 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -9,6 +9,8 @@ import { isClosingParenToken, isOpeningParenToken, isParenthesized, + nullThrows, + NullThrowsReasons, } from '../util'; import { getWrappedCode } from '../util/getWrappedCode'; @@ -109,14 +111,14 @@ export default createRule({ let afterCount = 0; if (isParenthesized(node, context.sourceCode)) { - const bodyOpeningParen = context.sourceCode.getTokenBefore( - node, - isOpeningParenToken, - )!; - const bodyClosingParen = context.sourceCode.getTokenAfter( - node, - isClosingParenToken, - )!; + const bodyOpeningParen = nullThrows( + context.sourceCode.getTokenBefore(node, isOpeningParenToken), + NullThrowsReasons.MissingToken('(', 'node'), + ); + const bodyClosingParen = nullThrows( + context.sourceCode.getTokenAfter(node, isClosingParenToken), + NullThrowsReasons.MissingToken(')', 'node'), + ); beforeCount = node.range[0] - bodyOpeningParen.range[0]; afterCount = bodyClosingParen.range[1] - node.range[1]; diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts index b4d0dbd52acc..23bc43603b34 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts @@ -529,7 +529,10 @@ export default createRule({ const last = namedSpecifierGroup[namedSpecifierGroup.length - 1]; const removeRange: TSESTree.Range = [first.range[0], last.range[1]]; const textRange: TSESTree.Range = [...removeRange]; - const before = context.sourceCode.getTokenBefore(first)!; + const before = nullThrows( + context.sourceCode.getTokenBefore(first), + NullThrowsReasons.MissingToken('token', 'first specifier'), + ); textRange[0] = before.range[1]; if (isCommaToken(before)) { removeRange[0] = before.range[0]; @@ -539,7 +542,10 @@ export default createRule({ const isFirst = allNamedSpecifiers[0] === first; const isLast = allNamedSpecifiers[allNamedSpecifiers.length - 1] === last; - const after = context.sourceCode.getTokenAfter(last)!; + const after = nullThrows( + context.sourceCode.getTokenAfter(last), + NullThrowsReasons.MissingToken('token', 'last specifier'), + ); textRange[1] = after.range[0]; if (isFirst || isLast) { if (isCommaToken(after)) { @@ -566,13 +572,20 @@ export default createRule({ ): TSESLint.RuleFix { const closingBraceToken = nullThrows( context.sourceCode.getFirstTokenBetween( - context.sourceCode.getFirstToken(target)!, + nullThrows( + context.sourceCode.getFirstToken(target), + NullThrowsReasons.MissingToken('token before', 'import'), + ), target.source, isClosingBraceToken, ), NullThrowsReasons.MissingToken('}', target.type), ); - const before = context.sourceCode.getTokenBefore(closingBraceToken)!; + const before = nullThrows( + context.sourceCode.getTokenBefore(closingBraceToken), + NullThrowsReasons.MissingToken('token before', 'closing brace'), + ); + if (!isCommaToken(before) && !isOpeningBraceToken(before)) { insertText = `,${insertText}`; } diff --git a/packages/eslint-plugin/src/rules/enum-utils/shared.ts b/packages/eslint-plugin/src/rules/enum-utils/shared.ts index b2a93b4f1162..f1de32ab8f95 100644 --- a/packages/eslint-plugin/src/rules/enum-utils/shared.ts +++ b/packages/eslint-plugin/src/rules/enum-utils/shared.ts @@ -12,12 +12,15 @@ import { isTypeFlagSet } from '../../util'; * - `Fruit.Apple` --> `Fruit` */ function getBaseEnumType(typeChecker: ts.TypeChecker, type: ts.Type): ts.Type { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const symbol = type.getSymbol()!; if (!tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.EnumMember)) { return type; } - return typeChecker.getTypeAtLocation(symbol.valueDeclaration!.parent); + return typeChecker.getTypeAtLocation( + (symbol.valueDeclaration as ts.EnumMember).parent, + ); } /** diff --git a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts index 52453371540c..83591c7fa71d 100644 --- a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts +++ b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts @@ -1,7 +1,12 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; -import { createRule, getNameFromMember } from '../util'; +import { + createRule, + getNameFromMember, + nullThrows, + NullThrowsReasons, +} from '../util'; type AccessibilityLevel = | 'explicit' // require an accessor (including public) @@ -182,7 +187,7 @@ export default createRule({ ): TSESLint.ReportFixFunction { return function (fixer: TSESLint.RuleFixer): TSESLint.RuleFix { const tokens = context.sourceCode.getTokens(node); - let rangeToRemove: TSESLint.AST.Range; + let rangeToRemove!: TSESLint.AST.Range; for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; if ( @@ -207,7 +212,7 @@ export default createRule({ } } } - return fixer.removeRange(rangeToRemove!); + return fixer.removeRange(rangeToRemove); }; } @@ -228,7 +233,10 @@ export default createRule({ ): TSESLint.RuleFix | null { if (node.decorators.length) { const lastDecorator = node.decorators[node.decorators.length - 1]; - const nextToken = context.sourceCode.getTokenAfter(lastDecorator)!; + const nextToken = nullThrows( + context.sourceCode.getTokenAfter(lastDecorator), + NullThrowsReasons.MissingToken('token', 'last decorator'), + ); return fixer.insertTextBefore(nextToken, `${accessibility} `); } return fixer.insertTextBefore(node, `${accessibility} `); diff --git a/packages/eslint-plugin/src/rules/func-call-spacing.ts b/packages/eslint-plugin/src/rules/func-call-spacing.ts index 4e87b7c6d076..80df92aaa701 100644 --- a/packages/eslint-plugin/src/rules/func-call-spacing.ts +++ b/packages/eslint-plugin/src/rules/func-call-spacing.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import { diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts index 1f25bd794515..9ebb23d75844 100644 --- a/packages/eslint-plugin/src/rules/indent.ts +++ b/packages/eslint-plugin/src/rules/indent.ts @@ -380,6 +380,7 @@ export default createRule({ }, TSMappedType(node: TSESTree.TSMappedType) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const squareBracketStart = context.sourceCode.getTokenBefore( node.typeParameter, )!; diff --git a/packages/eslint-plugin/src/rules/key-spacing.ts b/packages/eslint-plugin/src/rules/key-spacing.ts index 28ba361e7a76..7ca41df72cc0 100644 --- a/packages/eslint-plugin/src/rules/key-spacing.ts +++ b/packages/eslint-plugin/src/rules/key-spacing.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; diff --git a/packages/eslint-plugin/src/rules/keyword-spacing.ts b/packages/eslint-plugin/src/rules/keyword-spacing.ts index ff3878ec6340..7b47c8294260 100644 --- a/packages/eslint-plugin/src/rules/keyword-spacing.ts +++ b/packages/eslint-plugin/src/rules/keyword-spacing.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 1e731bbdf761..0c3fe87c6f17 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index 4b2bb87f5efa..136293141a9f 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -506,6 +506,7 @@ function getRankOrder( const stack = memberGroups.slice(); // Get a copy of the member groups while (stack.length > 0 && rank === -1) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const memberGroup = stack.shift()!; rank = orderConfig.findIndex(memberType => Array.isArray(memberType) @@ -993,6 +994,8 @@ export default createRule({ } } + // https://github.com/typescript-eslint/typescript-eslint/issues/5439 + /* eslint-disable @typescript-eslint/no-non-null-assertion */ return { ClassDeclaration(node): void { validateMembersOrder( @@ -1023,5 +1026,6 @@ export default createRule({ ); }, }; + /* eslint-enable @typescript-eslint/no-non-null-assertion */ }, }); 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 3ebea0ce8a9e..38df4fb0dc44 100644 --- a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts +++ b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts @@ -3,6 +3,7 @@ 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, @@ -133,14 +134,26 @@ export default createRule({ const arrowBodyText = context.sourceCode.getText(arrowBody); const newArrowBodyText = `{ ${arrowBodyText}; }`; if (isParenthesized(arrowBody, context.sourceCode)) { - const bodyOpeningParen = context.sourceCode.getTokenBefore( - arrowBody, - isOpeningParenToken, - )!; - const bodyClosingParen = context.sourceCode.getTokenAfter( - arrowBody, - isClosingParenToken, - )!; + const bodyOpeningParen = nullThrows( + context.sourceCode.getTokenBefore( + arrowBody, + isOpeningParenToken, + ), + NullThrowsReasons.MissingToken( + 'opening parenthesis', + 'arrow body', + ), + ); + const bodyClosingParen = nullThrows( + context.sourceCode.getTokenAfter( + arrowBody, + isClosingParenToken, + ), + NullThrowsReasons.MissingToken( + 'closing parenthesis', + 'arrow body', + ), + ); return fixer.replaceTextRange( [bodyOpeningParen.range[0], bodyClosingParen.range[1]], newArrowBodyText, @@ -163,25 +176,23 @@ export default createRule({ }); } - const returnStmt = invalidAncestor; - - if (isFinalReturn(returnStmt)) { + if (isFinalReturn(invalidAncestor)) { // remove the `return` keyword return context.report({ node, messageId: 'invalidVoidExprReturnLast', fix(fixer) { - if (!canFix(returnStmt)) { + if (!canFix(invalidAncestor)) { return null; } - const returnValue = returnStmt.argument!; + const returnValue = invalidAncestor.argument; const returnValueText = context.sourceCode.getText(returnValue); let newReturnStmtText = `${returnValueText};`; if (isPreventingASI(returnValue)) { // put a semicolon at the beginning of the line newReturnStmtText = `;${newReturnStmtText}`; } - return fixer.replaceText(returnStmt, newReturnStmtText); + return fixer.replaceText(invalidAncestor, newReturnStmtText); }, }); } @@ -191,19 +202,21 @@ export default createRule({ node, messageId: 'invalidVoidExprReturn', fix(fixer) { - const returnValue = returnStmt.argument!; + const returnValue = invalidAncestor.argument; const returnValueText = context.sourceCode.getText(returnValue); let newReturnStmtText = `${returnValueText}; return;`; if (isPreventingASI(returnValue)) { // put a semicolon at the beginning of the line newReturnStmtText = `;${newReturnStmtText}`; } - if (returnStmt.parent.type !== AST_NODE_TYPES.BlockStatement) { + if ( + invalidAncestor.parent.type !== AST_NODE_TYPES.BlockStatement + ) { // e.g. `if (cond) return console.error();` // add braces if not inside a block newReturnStmtText = `{ ${newReturnStmtText} }`; } - return fixer.replaceText(returnStmt, newReturnStmtText); + return fixer.replaceText(invalidAncestor, newReturnStmtText); }, }); } @@ -225,6 +238,15 @@ export default createRule({ }, }; + type ReturnStatementWithArgument = MakeRequired< + TSESTree.ReturnStatement, + 'argument' + >; + + type InvalidAncestor = + | Exclude + | ReturnStatementWithArgument; + /** * Inspects the void expression's ancestors and finds closest invalid one. * By default anything other than an ExpressionStatement is invalid. @@ -233,7 +255,7 @@ export default createRule({ * @param node The void expression node to check. * @returns Invalid ancestor node if it was found. `null` otherwise. */ - function findInvalidAncestor(node: TSESTree.Node): TSESTree.Node | null { + function findInvalidAncestor(node: TSESTree.Node): InvalidAncestor | null { const parent = nullThrows(node.parent, NullThrowsReasons.MissingParent); if (parent.type === AST_NODE_TYPES.SequenceExpression) { if (node !== parent.expressions[parent.expressions.length - 1]) { @@ -286,8 +308,9 @@ export default createRule({ return findInvalidAncestor(parent); } - // any other parent is invalid - return parent; + // Any other parent is invalid. + // We can assume a return statement will have an argument. + return parent as InvalidAncestor; } /** Checks whether the return statement is the last statement in a function body. */ @@ -341,13 +364,13 @@ export default createRule({ } function canFix( - node: TSESTree.ReturnStatement | TSESTree.ArrowFunctionExpression, + node: ReturnStatementWithArgument | TSESTree.ArrowFunctionExpression, ): boolean { const services = getParserServices(context); const targetNode = node.type === AST_NODE_TYPES.ReturnStatement - ? node.argument! + ? node.argument : node.body; const type = getConstrainedTypeAtLocation(services, targetNode); diff --git a/packages/eslint-plugin/src/rules/no-dynamic-delete.ts b/packages/eslint-plugin/src/rules/no-dynamic-delete.ts index 35c9b65937af..886b63cff73a 100644 --- a/packages/eslint-plugin/src/rules/no-dynamic-delete.ts +++ b/packages/eslint-plugin/src/rules/no-dynamic-delete.ts @@ -2,7 +2,7 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; -import { createRule } from '../util'; +import { createRule, nullThrows, NullThrowsReasons } from '../util'; export default createRule({ name: 'no-dynamic-delete', @@ -67,8 +67,14 @@ export default createRule({ function getTokenRange(property: TSESTree.Expression): [number, number] { return [ - context.sourceCode.getTokenBefore(property)!.range[0], - context.sourceCode.getTokenAfter(property)!.range[1], + nullThrows( + context.sourceCode.getTokenBefore(property), + NullThrowsReasons.MissingToken('token before', 'property'), + ).range[0], + nullThrows( + context.sourceCode.getTokenAfter(property), + NullThrowsReasons.MissingToken('token after', 'property'), + ).range[1], ]; } }, diff --git a/packages/eslint-plugin/src/rules/no-inferrable-types.ts b/packages/eslint-plugin/src/rules/no-inferrable-types.ts index f332642a339c..7455675f6ac7 100644 --- a/packages/eslint-plugin/src/rules/no-inferrable-types.ts +++ b/packages/eslint-plugin/src/rules/no-inferrable-types.ts @@ -2,7 +2,7 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule } from '../util'; +import { createRule, nullThrows, NullThrowsReasons } from '../util'; type Options = [ { @@ -223,7 +223,12 @@ export default createRule({ node.left.optional) || (node.type === AST_NODE_TYPES.PropertyDefinition && node.definite) ) { - yield fixer.remove(context.sourceCode.getTokenBefore(typeNode)!); + yield fixer.remove( + nullThrows( + context.sourceCode.getTokenBefore(typeNode), + NullThrowsReasons.MissingToken('token before', 'type node'), + ), + ); } yield fixer.remove(typeNode); }, 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 3d92174c95af..8ae1604a210b 100644 --- a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts +++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts @@ -205,6 +205,8 @@ export default createRule<[Options], MessageIds>({ // default cases if ( validParents.includes(node.parent.type) && + // https://github.com/typescript-eslint/typescript-eslint/issues/6225 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion !invalidGrandParents.includes(node.parent.parent!.type) ) { return; diff --git a/packages/eslint-plugin/src/rules/no-mixed-enums.ts b/packages/eslint-plugin/src/rules/no-mixed-enums.ts index c9bd94036f09..e8a06d9861e0 100644 --- a/packages/eslint-plugin/src/rules/no-mixed-enums.ts +++ b/packages/eslint-plugin/src/rules/no-mixed-enums.ts @@ -160,10 +160,12 @@ export default createRule({ node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration && node.parent.parent.type === AST_NODE_TYPES.TSModuleBlock ) { + // https://github.com/typescript-eslint/typescript-eslint/issues/8352 // TODO: We don't need to dip into the TypeScript type checker here! // Merged namespaces must all exist in the same file. // We could instead compare this file's nodes to find the merges. const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.id); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const declarations = typeChecker .getSymbolAtLocation(tsNode)! .getDeclarations()!; 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 62bd5f1ab890..8545b0e1c110 100644 --- a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts @@ -1,7 +1,12 @@ import type { TSESLint } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule, isNonNullAssertionPunctuator } from '../util'; +import { + createRule, + isNonNullAssertionPunctuator, + nullThrows, + NullThrowsReasons, +} from '../util'; type MessageIds = 'noNonNull' | 'suggestOptionalChain'; @@ -29,10 +34,13 @@ export default createRule<[], MessageIds>({ const suggest: TSESLint.ReportSuggestionArray = []; // it always exists in non-null assertion - const nonNullOperator = context.sourceCode.getTokenAfter( - node.expression, - isNonNullAssertionPunctuator, - )!; + const nonNullOperator = nullThrows( + context.sourceCode.getTokenAfter( + node.expression, + isNonNullAssertionPunctuator, + ), + NullThrowsReasons.MissingToken('!', 'expression'), + ); function replaceTokenWithOptional(): TSESLint.ReportFixFunction { return fixer => fixer.replaceText(nonNullOperator, '?.'); @@ -60,8 +68,10 @@ export default createRule<[], MessageIds>({ fix(fixer) { // x!.y?.z // ^ punctuator - const punctuator = - context.sourceCode.getTokenAfter(nonNullOperator)!; + const punctuator = nullThrows( + context.sourceCode.getTokenAfter(nonNullOperator), + NullThrowsReasons.MissingToken('.', '!'), + ); return [ fixer.remove(nonNullOperator), fixer.insertTextBefore(punctuator, '?'), diff --git a/packages/eslint-plugin/src/rules/no-shadow.ts b/packages/eslint-plugin/src/rules/no-shadow.ts index 628982acae0b..6a3a75de0a6d 100644 --- a/packages/eslint-plugin/src/rules/no-shadow.ts +++ b/packages/eslint-plugin/src/rules/no-shadow.ts @@ -282,6 +282,7 @@ export default createRule({ * @returns Whether or not the variable name is allowed. */ function isAllowed(variable: TSESLint.Scope.Variable): boolean { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return options.allow!.includes(variable.name); } @@ -650,6 +651,7 @@ export default createRule({ const stack = globalScope.childScopes.slice(); while (stack.length) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const scope = stack.pop()!; stack.push(...scope.childScopes); diff --git a/packages/eslint-plugin/src/rules/no-this-alias.ts b/packages/eslint-plugin/src/rules/no-this-alias.ts index ece937cff3df..98006e56328d 100644 --- a/packages/eslint-plugin/src/rules/no-this-alias.ts +++ b/packages/eslint-plugin/src/rules/no-this-alias.ts @@ -65,7 +65,9 @@ export default createRule({ const hasAllowedName = id.type === AST_NODE_TYPES.Identifier - ? allowedNames!.includes(id.name) + ? // https://github.com/typescript-eslint/typescript-eslint/issues/5439 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + allowedNames!.includes(id.name) : false; if (!hasAllowedName) { context.report({ diff --git a/packages/eslint-plugin/src/rules/no-type-alias.ts b/packages/eslint-plugin/src/rules/no-type-alias.ts index b72c1f8b991a..53e64a3b5c69 100644 --- a/packages/eslint-plugin/src/rules/no-type-alias.ts +++ b/packages/eslint-plugin/src/rules/no-type-alias.ts @@ -257,6 +257,8 @@ export default createRule({ type: TypeWithLabel, isTopLevel = false, ): void { + // https://github.com/typescript-eslint/typescript-eslint/issues/5439 + /* eslint-disable @typescript-eslint/no-non-null-assertion */ if (type.node.type === AST_NODE_TYPES.TSFunctionType) { // callback if (allowCallbacks === 'never') { @@ -309,6 +311,7 @@ export default createRule({ // unhandled type - shouldn't happen reportError(type.node, type.compositionType, isTopLevel, 'Unhandled'); } + /* eslint-enable @typescript-eslint/no-non-null-assertion */ } /** 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 4d79c84368e0..9212ed15d5f6 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -11,6 +11,8 @@ import { getParserServices, isNullableType, isTypeFlagSet, + nullThrows, + NullThrowsReasons, } from '../util'; type Options = [ @@ -261,18 +263,25 @@ export default createRule({ messageId: 'unnecessaryAssertion', fix(fixer) { if (node.type === AST_NODE_TYPES.TSTypeAssertion) { - const openingAngleBracket = context.sourceCode.getTokenBefore( - node.typeAnnotation, - token => - token.type === AST_TOKEN_TYPES.Punctuator && - token.value === '<', - )!; - const closingAngleBracket = context.sourceCode.getTokenAfter( - node.typeAnnotation, - token => - token.type === AST_TOKEN_TYPES.Punctuator && - token.value === '>', - )!; + 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([ @@ -281,15 +290,22 @@ export default createRule({ ]); } // `as` is always present in TSAsExpression - const asToken = context.sourceCode.getTokenAfter( - node.expression, - token => - token.type === AST_TOKEN_TYPES.Identifier && - token.value === 'as', - )!; - const tokenBeforeAs = context.sourceCode.getTokenBefore(asToken, { - includeComments: true, - })!; + 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]]); 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 77ea5c874e91..1c166706a7c0 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-constraint.ts @@ -3,11 +3,9 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { extname } from 'path'; import * as ts from 'typescript'; +import type { MakeRequired } from '../util'; import { createRule } from '../util'; -type MakeRequired = Omit & { - [K in Key]-?: NonNullable; -}; type TypeParameterWithConstraint = MakeRequired< TSESTree.TSTypeParameter, 'constraint' diff --git a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts index 4ce2f6db99a3..fc1008773aab 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts @@ -338,11 +338,11 @@ export default createRule({ } }, 'PropertyDefinition[value != null]'( - node: TSESTree.PropertyDefinition, + node: TSESTree.PropertyDefinition & { value: NonNullable }, ): void { checkAssignment( node.key, - node.value!, + node.value, node, getComparisonType(node.typeAnnotation), ); diff --git a/packages/eslint-plugin/src/rules/object-curly-spacing.ts b/packages/eslint-plugin/src/rules/object-curly-spacing.ts index e84677f01a1f..4edd5a688f99 100644 --- a/packages/eslint-plugin/src/rules/object-curly-spacing.ts +++ b/packages/eslint-plugin/src/rules/object-curly-spacing.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; diff --git a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts index 1e84a836190a..20d6b8e1dfd4 100644 --- a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts +++ b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; diff --git a/packages/eslint-plugin/src/rules/parameter-properties.ts b/packages/eslint-plugin/src/rules/parameter-properties.ts index d72a2591c415..5246594e912e 100644 --- a/packages/eslint-plugin/src/rules/parameter-properties.ts +++ b/packages/eslint-plugin/src/rules/parameter-properties.ts @@ -1,7 +1,7 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule } from '../util'; +import { createRule, nullThrows } from '../util'; type Modifier = | 'private readonly' @@ -174,7 +174,10 @@ export default createRule({ }, ':matches(ClassDeclaration, ClassExpression):exit'(): void { - const propertyNodesByName = propertyNodesByNameStack.pop()!; + const propertyNodesByName = nullThrows( + propertyNodesByNameStack.pop(), + 'Stack should exist on class exit', + ); for (const [name, nodes] of propertyNodesByName) { if ( diff --git a/packages/eslint-plugin/src/rules/prefer-function-type.ts b/packages/eslint-plugin/src/rules/prefer-function-type.ts index e85ae87df8e3..1743f80b9fde 100644 --- a/packages/eslint-plugin/src/rules/prefer-function-type.ts +++ b/packages/eslint-plugin/src/rules/prefer-function-type.ts @@ -102,6 +102,8 @@ export default createRule({ : (fixer: TSESLint.RuleFixer): TSESLint.RuleFix[] => { const fixes: TSESLint.RuleFix[] = []; const start = member.range[0]; + // https://github.com/microsoft/TypeScript/pull/56908 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const colonPos = member.returnType!.range[0] - start; const text = context.sourceCode .getText() diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index 9faa2d52a3db..8a9bfae1928e 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -321,6 +321,8 @@ export default createRule({ return; } + // https://github.com/typescript-eslint/typescript-eslint/issues/5439 + /* eslint-disable @typescript-eslint/no-non-null-assertion */ const ignorableFlags = [ (ignorePrimitives === true || ignorePrimitives!.bigint) && ts.TypeFlags.BigInt, @@ -342,6 +344,7 @@ export default createRule({ ) { return; } + /* eslint-enable @typescript-eslint/no-non-null-assertion */ const barBarOperator = nullThrows( context.sourceCode.getTokenAfter( 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 1b7de382d47d..3803b1298b4a 100644 --- a/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts +++ b/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts @@ -132,6 +132,7 @@ export default createRule({ } // reject param is always present in variables declared by executor + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const rejectVariable = context.sourceCode .getDeclaredVariables(executor) .find(variable => variable.identifiers.includes(rejectParamNode))!; 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 72b73a883520..6cb935c1db9a 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly-parameter-types.ts @@ -114,7 +114,7 @@ export default createRule({ const type = services.getTypeAtLocation(actualParam); const isReadOnly = isTypeReadonly(services.program, type, { - treatMethodsAsReadonly: treatMethodsAsReadonly!, + treatMethodsAsReadonly: !!treatMethodsAsReadonly, allow, }); diff --git a/packages/eslint-plugin/src/rules/prefer-readonly.ts b/packages/eslint-plugin/src/rules/prefer-readonly.ts index c1748195b392..80f398394965 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly.ts @@ -3,7 +3,12 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; -import { createRule, getParserServices, typeIsOrHasBaseType } from '../util'; +import { + createRule, + getParserServices, + nullThrows, + typeIsOrHasBaseType, +} from '../util'; type MessageIds = 'preferReadonly'; type Options = [ @@ -183,7 +188,10 @@ export default createRule({ ); }, 'ClassDeclaration, ClassExpression:exit'(): void { - const finalizedClassScope = classScopeStack.pop()!; + const finalizedClassScope = nullThrows( + classScopeStack.pop(), + 'Stack should exist on class exit', + ); for (const violatingNode of finalizedClassScope.finalizeUnmodifiedPrivateNonReadonlys()) { const { esNode, nameNode } = 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 7cf6976e3e67..70d2b7ae6790 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 @@ -211,10 +211,10 @@ export default createRule({ function getPropertyRange( node: TSESTree.MemberExpression, ): [number, number] { - const dotOrOpenBracket = context.sourceCode.getTokenAfter( - node.object, - isNotClosingParenToken, - )!; + const dotOrOpenBracket = nullThrows( + context.sourceCode.getTokenAfter(node.object, isNotClosingParenToken), + NullThrowsReasons.MissingToken('closing parenthesis', 'member'), + ); return [dotOrOpenBracket.range[0], node.range[1]]; } diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index e9dfabc3ca80..c191ce45b0db 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -8,6 +8,8 @@ import { getFunctionHeadLoc, getParserServices, isTypeFlagSet, + nullThrows, + NullThrowsReasons, } from '../util'; type Options = [ @@ -94,6 +96,8 @@ export default createRule({ ) { const allAllowedPromiseNames = new Set([ 'Promise', + // https://github.com/typescript-eslint/typescript-eslint/issues/5439 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...allowedPromiseNames!, ]); const services = getParserServices(context); @@ -114,6 +118,7 @@ export default createRule({ if ( !containsAllTypesByName( returnType, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion allowAny!, allAllowedPromiseNames, // If no return type is explicitly set, we check if any parts of the return type match a Promise (instead of requiring all to match). @@ -160,7 +165,10 @@ export default createRule({ const method = node.parent; // the token to put `async` before - let keyToken = context.sourceCode.getFirstToken(method)!; + let keyToken = nullThrows( + context.sourceCode.getFirstToken(method), + NullThrowsReasons.MissingToken('key token', 'method'), + ); // if there are decorators then skip past them if ( @@ -169,7 +177,10 @@ export default createRule({ ) { const lastDecorator = method.decorators[method.decorators.length - 1]; - keyToken = context.sourceCode.getTokenAfter(lastDecorator)!; + keyToken = nullThrows( + context.sourceCode.getTokenAfter(lastDecorator), + NullThrowsReasons.MissingToken('key token', 'last decorator'), + ); } // if current token is a keyword like `static` or `public` then skip it @@ -177,12 +188,18 @@ export default createRule({ keyToken.type === AST_TOKEN_TYPES.Keyword && keyToken.range[0] < method.key.range[0] ) { - keyToken = context.sourceCode.getTokenAfter(keyToken)!; + keyToken = nullThrows( + context.sourceCode.getTokenAfter(keyToken), + NullThrowsReasons.MissingToken('token', 'keyword'), + ); } // check if there is a space between key and previous token const insertSpace = !context.sourceCode.isSpaceBetween( - context.sourceCode.getTokenBefore(keyToken)!, + nullThrows( + context.sourceCode.getTokenBefore(keyToken), + NullThrowsReasons.MissingToken('token', 'keyword'), + ), keyToken, ); diff --git a/packages/eslint-plugin/src/rules/space-before-function-paren.ts b/packages/eslint-plugin/src/rules/space-before-function-paren.ts index f0d1c6082017..78ea98239db7 100644 --- a/packages/eslint-plugin/src/rules/space-before-function-paren.ts +++ b/packages/eslint-plugin/src/rules/space-before-function-paren.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; diff --git a/packages/eslint-plugin/src/rules/space-infix-ops.ts b/packages/eslint-plugin/src/rules/space-infix-ops.ts index b9094ad29b64..432f7907b270 100644 --- a/packages/eslint-plugin/src/rules/space-infix-ops.ts +++ b/packages/eslint-plugin/src/rules/space-infix-ops.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { AST_TOKEN_TYPES, TSESTree } from '@typescript-eslint/utils'; import type { diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index c5fea6021ad2..01e15d3d88c5 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -244,7 +244,10 @@ export default createRule({ * unless `isCondition` flag is set to false, in which case * it's assumed to be used for side effects only and is skipped. */ - function traverseNode(node: TSESTree.Node, isCondition: boolean): void { + function traverseNode( + node: TSESTree.Expression, + isCondition: boolean, + ): void { // prevent checking the same node multiple times if (traversedNodes.has(node)) { return; @@ -272,7 +275,7 @@ export default createRule({ * This function does the actual type check on a node. * It analyzes the type of a node and checks if it is allowed in a boolean context. */ - function checkNode(node: TSESTree.Node): void { + function checkNode(node: TSESTree.Expression): void { const type = getConstrainedTypeAtLocation(services, node); const types = inspectVariantTypes(tsutils.unionTypeParts(type)); @@ -307,7 +310,7 @@ export default createRule({ // nullable boolean if (is('nullish', 'boolean')) { if (!options.allowNullableBoolean) { - if (isLogicalNegationExpression(node.parent!)) { + if (isLogicalNegationExpression(node.parent)) { // if (!nullableBoolean) context.report({ node, @@ -372,7 +375,7 @@ export default createRule({ // string if (is('string') || is('truthy string')) { if (!options.allowString) { - if (isLogicalNegationExpression(node.parent!)) { + if (isLogicalNegationExpression(node.parent)) { // if (!string) context.report({ node, @@ -447,7 +450,7 @@ export default createRule({ // nullable string if (is('nullish', 'string')) { if (!options.allowNullableString) { - if (isLogicalNegationExpression(node.parent!)) { + if (isLogicalNegationExpression(node.parent)) { // if (!nullableString) context.report({ node, @@ -546,7 +549,7 @@ export default createRule({ }), }); } - } else if (isLogicalNegationExpression(node.parent!)) { + } else if (isLogicalNegationExpression(node.parent)) { // if (!number) context.report({ node, @@ -623,7 +626,7 @@ export default createRule({ // nullable number if (is('nullish', 'number')) { if (!options.allowNullableNumber) { - if (isLogicalNegationExpression(node.parent!)) { + if (isLogicalNegationExpression(node.parent)) { // if (!nullableNumber) context.report({ node, @@ -704,7 +707,7 @@ export default createRule({ // nullable object if (is('nullish', 'object')) { if (!options.allowNullableObject) { - if (isLogicalNegationExpression(node.parent!)) { + if (isLogicalNegationExpression(node.parent)) { // if (!nullableObject) context.report({ node, @@ -755,7 +758,7 @@ export default createRule({ is('nullish', 'number', 'string', 'enum') ) { if (!options.allowNullableEnum) { - if (isLogicalNegationExpression(node.parent!)) { + if (isLogicalNegationExpression(node.parent)) { context.report({ node, messageId: 'conditionErrorNullableEnum', diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 34b5d90be1c6..1ea0b7c78cb7 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -8,6 +8,8 @@ import { getParserServices, isClosingBraceToken, isOpeningBraceToken, + nullThrows, + NullThrowsReasons, requiresQuoting, } from '../util'; @@ -212,7 +214,8 @@ export default createRule({ missingBranchType, ts.TypeFlags.ESSymbolLike, ) - ? missingBranchName! + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + missingBranchName! : checker.typeToString(missingBranchType); if ( @@ -245,14 +248,20 @@ export default createRule({ } // There were no existing cases. - const openingBrace = context.sourceCode.getTokenAfter( - node.discriminant, - isOpeningBraceToken, - )!; - const closingBrace = context.sourceCode.getTokenAfter( - node.discriminant, - isClosingBraceToken, - )!; + const openingBrace = nullThrows( + context.sourceCode.getTokenAfter( + node.discriminant, + isOpeningBraceToken, + ), + NullThrowsReasons.MissingToken('{', 'discriminant'), + ); + const closingBrace = nullThrows( + context.sourceCode.getTokenAfter( + node.discriminant, + isClosingBraceToken, + ), + NullThrowsReasons.MissingToken('}', 'discriminant'), + ); return fixer.replaceTextRange( [openingBrace.range[0], closingBrace.range[1]], diff --git a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts index 62dcd7495cb2..47fbf80d8ac0 100644 --- a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts +++ b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts @@ -167,9 +167,11 @@ export default createRule({ typeAnnotation: TSESTree.TypeNode, ): void { const nextToken = typeAnnotation; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const punctuatorTokenEnd = context.sourceCode.getTokenBefore(nextToken)!; let punctuatorTokenStart = punctuatorTokenEnd; let previousToken = + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion context.sourceCode.getTokenBefore(punctuatorTokenEnd)!; let type = punctuatorTokenEnd.value; @@ -202,12 +204,14 @@ export default createRule({ // shift the start to the ? type = '?:'; punctuatorTokenStart = previousToken; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion previousToken = context.sourceCode.getTokenBefore(previousToken)!; // handle the +/- modifiers for optional modification operators if (previousToken.value === '+' || previousToken.value === '-') { type = `${previousToken.value}?:`; punctuatorTokenStart = previousToken; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion previousToken = context.sourceCode.getTokenBefore(previousToken)!; } } diff --git a/packages/eslint-plugin/src/rules/unified-signatures.ts b/packages/eslint-plugin/src/rules/unified-signatures.ts index 08ea21c58282..c8fa2f85f7df 100644 --- a/packages/eslint-plugin/src/rules/unified-signatures.ts +++ b/packages/eslint-plugin/src/rules/unified-signatures.ts @@ -2,7 +2,7 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { Equal } from '../util'; -import { arraysAreEqual, createRule } from '../util'; +import { arraysAreEqual, createRule, nullThrows } from '../util'; interface Failure { unify: Unify; @@ -508,9 +508,13 @@ export default createRule({ } function checkScope(): void { + const scope = nullThrows( + currentScope, + 'checkScope() called without a current scope', + ); const failures = checkOverloads( - Array.from(currentScope!.overloads.values()), - currentScope!.typeParameters, + Array.from(scope.overloads.values()), + scope.typeParameters, ); addFailures(failures); currentScope = scopes.pop(); diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index dbe749e62900..5bbddb611b36 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -136,6 +136,7 @@ class UnusedVarsVisitor< let node: TSESTree.Node; if (typeof variableOrIdentifierOrName === 'string') { name = variableOrIdentifierOrName; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion node = parent!; } else { name = variableOrIdentifierOrName.name; @@ -429,11 +430,13 @@ function isExported(variable: TSESLint.Scope.Variable): boolean { let node = definition.node; if (node.type === AST_NODE_TYPES.VariableDeclarator) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion node = node.parent!; } else if (definition.type === TSESLint.Scope.DefinitionType.Parameter) { return false; } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return node.parent!.type.indexOf('Export') === 0; }); } @@ -563,8 +566,10 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { const id = ref.identifier; const parent = id.parent; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const grandparent = parent.parent!; const refScope = ref.from.variableScope; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const varScope = ref.resolved!.scope.variableScope; const canBeUsedLater = refScope !== varScope || isInLoop(id); @@ -696,6 +701,8 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { const id = ref.identifier; const parent = id.parent; + // https://github.com/typescript-eslint/typescript-eslint/issues/6225 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const grandparent = parent.parent!; return ( diff --git a/packages/eslint-plugin/src/util/getOperatorPrecedence.ts b/packages/eslint-plugin/src/util/getOperatorPrecedence.ts index 3d3152f436f5..19dec0e1dc99 100644 --- a/packages/eslint-plugin/src/util/getOperatorPrecedence.ts +++ b/packages/eslint-plugin/src/util/getOperatorPrecedence.ts @@ -3,6 +3,8 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { SyntaxKind } from 'typescript'; +import type { ValueOf } from './types'; + export enum OperatorPrecedence { // Expression: // AssignmentExpression @@ -293,10 +295,10 @@ export function getOperatorPrecedenceForNode( } } -type ValueOf = T[keyof T]; type TSESTreeOperatorKind = | ValueOf | ValueOf; + export function getOperatorPrecedence( nodeKind: SyntaxKind, operatorKind: SyntaxKind, diff --git a/packages/eslint-plugin/src/util/getWrappingFixer.ts b/packages/eslint-plugin/src/util/getWrappingFixer.ts index 043d144ec5cb..d5d07b6ba7e1 100644 --- a/packages/eslint-plugin/src/util/getWrappingFixer.ts +++ b/packages/eslint-plugin/src/util/getWrappingFixer.ts @@ -1,5 +1,9 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; +import { + AST_NODE_TYPES, + ASTUtils, + ESLintUtils, +} from '@typescript-eslint/utils'; interface WrappingFixerParams { /** Source code. */ @@ -94,6 +98,7 @@ export function isStrongPrecedenceNode(innerNode: TSESTree.Node): boolean { * Check if a node's parent could have different precedence if the node changes. */ function isWeakPrecedenceParent(node: TSESTree.Node): boolean { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const parent = node.parent!; if ( @@ -141,6 +146,8 @@ function isMissingSemicolonBefore( sourceCode: TSESLint.SourceCode, ): boolean { for (;;) { + // https://github.com/typescript-eslint/typescript-eslint/issues/6225 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const parent = node.parent!; if (parent.type === AST_NODE_TYPES.ExpressionStatement) { @@ -154,7 +161,10 @@ function isMissingSemicolonBefore( const previousStatement = block.body[statementIndex - 1]; if ( statementIndex > 0 && - sourceCode.getLastToken(previousStatement)!.value !== ';' + ESLintUtils.nullThrows( + sourceCode.getLastToken(previousStatement), + 'Mismatched semicolon and block', + ).value !== ';' ) { return true; } @@ -173,6 +183,8 @@ function isMissingSemicolonBefore( * Checks if a node is LHS of an operator. */ function isLeftHandSide(node: TSESTree.Node): boolean { + // https://github.com/typescript-eslint/typescript-eslint/issues/6225 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const parent = node.parent!; // a++ diff --git a/packages/eslint-plugin/src/util/index.ts b/packages/eslint-plugin/src/util/index.ts index 5e9994a136d9..f6b583000ce5 100644 --- a/packages/eslint-plugin/src/util/index.ts +++ b/packages/eslint-plugin/src/util/index.ts @@ -14,6 +14,7 @@ export * from './isNullLiteral'; export * from './isUndefinedIdentifier'; export * from './misc'; export * from './objectIterators'; +export * from './types'; // this is done for convenience - saves migrating all of the old rules export * from '@typescript-eslint/type-utils'; diff --git a/packages/eslint-plugin/src/util/types.ts b/packages/eslint-plugin/src/util/types.ts new file mode 100644 index 000000000000..84991966a237 --- /dev/null +++ b/packages/eslint-plugin/src/util/types.ts @@ -0,0 +1,5 @@ +export type MakeRequired = Omit & { + [K in Key]-?: NonNullable; +}; + +export type ValueOf = T[keyof T]; diff --git a/packages/eslint-plugin/tests/rules/indent/utils.ts b/packages/eslint-plugin/tests/rules/indent/utils.ts index ae0b6e502ed0..19a108063dbf 100644 --- a/packages/eslint-plugin/tests/rules/indent/utils.ts +++ b/packages/eslint-plugin/tests/rules/indent/utils.ts @@ -1,17 +1,6 @@ // The following code is adapted from the the code in eslint. // License: https://github.com/eslint/eslint/blob/48700fc8408f394887cdedd071b22b757700fdcb/LICENSE -import type { - AST_NODE_TYPES, - AST_TOKEN_TYPES, - TSESLint, -} from '@typescript-eslint/utils'; - -import type rule from '../../../src/rules/indent'; -import type { InferMessageIdsTypeFromRule } from '../../../src/util'; - -type MessageIds = InferMessageIdsTypeFromRule; - /** * Prevents leading spaces in a multiline template literal from appearing in the resulting string * @param strings The strings in the template literal @@ -26,80 +15,9 @@ export function unIndent(strings: TemplateStringsArray): string { .split('\n'); const lineIndents = lines .filter(line => line.trim()) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion .map(line => WHITESPACE_REGEX.exec(line)![0].length); const minLineIndent = Math.min(...lineIndents); return lines.map(line => line.slice(minLineIndent)).join('\n'); } - -type ProvidedError = [ - // line number - number, - // expected indent - number | string, - // actual indent - number | string, - // node type - AST_NODE_TYPES | AST_TOKEN_TYPES, -]; - -function is2DProvidedErrorArr( - providedErrors?: ProvidedError | ProvidedError[], -): providedErrors is ProvidedError[] { - return !!providedErrors && Array.isArray(providedErrors[0]); -} - -/** - * Create error message object for failure cases with a single 'found' indentation type - * @param providedErrors error info - * @returns returns the error messages collection - */ -export function expectedErrors( - providedErrors: ProvidedError | ProvidedError[], -): TSESLint.TestCaseError[]; -/** - * Create error message object for failure cases with a single 'found' indentation type - * @param providedIndentType indent type of string or tab - * @param providedErrors error info - * @returns returns the error messages collection - */ -export function expectedErrors( - providedIndentType: string, - providedErrors: ProvidedError | ProvidedError[], -): TSESLint.TestCaseError[]; -export function expectedErrors( - providedIndentType: ProvidedError | ProvidedError[] | string, - providedErrors?: ProvidedError | ProvidedError[], -): TSESLint.TestCaseError[] { - let indentType: string; - let errors: ProvidedError[]; - - if (Array.isArray(providedIndentType)) { - errors = is2DProvidedErrorArr(providedIndentType) - ? providedIndentType - : [providedIndentType]; - indentType = 'space'; - } else { - errors = is2DProvidedErrorArr(providedErrors) - ? providedErrors - : [providedErrors!]; - indentType = providedIndentType; - } - - return errors.map(err => { - const [line, expected, actual, type] = err; - - return { - messageId: 'wrongIndentation', - data: { - expected: - typeof expected === 'string' && typeof actual === 'string' - ? expected - : `${expected} ${indentType}${expected === 1 ? '' : 's'}`, - actual, - }, - type, - line, - }; - }); -} diff --git a/packages/eslint-plugin/tools/generate-breaking-changes.mts b/packages/eslint-plugin/tools/generate-breaking-changes.mts index 05f4d8d5485a..dd57b2042bc5 100644 --- a/packages/eslint-plugin/tools/generate-breaking-changes.mts +++ b/packages/eslint-plugin/tools/generate-breaking-changes.mts @@ -1,8 +1,13 @@ import type { TypeScriptESLintRules } from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/rules'; +import type { RuleModule } from '@typescript-eslint/utils/ts-eslint'; import { fetch } from 'cross-fetch'; // markdown-table is ESM, hence this file needs to be `.mts` import { markdownTable } from 'markdown-table'; +type RuleModuleWithDocs = RuleModule & { + meta: { docs: object }; +}; + async function main(): Promise { const rulesImport = await import('../src/rules/index.js'); /* @@ -10,8 +15,11 @@ async function main(): Promise { { default: { default: Rules }} instead of just { default: Rules } - @ts-expect-error */ - const rules = rulesImport.default as TypeScriptESLintRules; + */ + const rules = rulesImport.default as unknown as Record< + string, + RuleModuleWithDocs + >; // Annotate which rules are new since the last version async function getNewRulesAsOfMajorVersion( @@ -131,7 +139,7 @@ async function main(): Promise { ...Object.entries(rules).map(([ruleName, { meta }]) => { const { deprecated } = meta; const { extendsBaseRule, recommended, requiresTypeChecking } = - meta.docs!; + meta.docs; return [ `[\`${ruleName}\`](https://typescript-eslint.io/rules/${ruleName})`, diff --git a/packages/repo-tools/src/generate-configs.mts b/packages/repo-tools/src/generate-configs.mts index 941d51fd4928..870b60c6de07 100644 --- a/packages/repo-tools/src/generate-configs.mts +++ b/packages/repo-tools/src/generate-configs.mts @@ -133,6 +133,7 @@ async function main(): Promise { settings.baseRuleForExtensionRule !== 'exclude' && BASE_RULES_TO_BE_OVERRIDDEN.has(key) ) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const baseRuleName = BASE_RULES_TO_BE_OVERRIDDEN.get(key)!; console.log( baseRuleName diff --git a/packages/repo-tools/src/generate-lib.mts b/packages/repo-tools/src/generate-lib.mts index a66ccea5db32..bc8f7f5e3ce1 100644 --- a/packages/repo-tools/src/generate-lib.mts +++ b/packages/repo-tools/src/generate-lib.mts @@ -93,6 +93,7 @@ function sanitize(name: string): string { } function getVariablesFromScope(scopeManager: ScopeManager): Variable[] { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const scope = scopeManager.globalScope!.childScopes[0]; const variables: Variable[] = []; for (const variable of scope.variables) { @@ -106,9 +107,9 @@ function getVariablesFromScope(scopeManager: ScopeManager): Variable[] { const REFERENCE_REGEX = /\/ /; function getReferences( - ast: TSESTree.Program & { comments?: TSESTree.Comment[] }, + ast: TSESTree.Program & { comments: TSESTree.Comment[] }, ): Set { - const comments = ast.comments!.filter( + const comments = ast.comments.filter( c => c.type === AST_TOKEN_TYPES.Line && c.value.startsWith('/