Skip to content

⬅️ Back to Table of Contents

📄 consistent-type-assertions.ts

📊 Analysis Summary

Metric Count
🔧 Functions 15
📦 Imports 10
📊 Variables & Constants 3
📑 Type Aliases 4

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin/src/rules/consistent-type-assertions.ts

📦 Imports

Name Source
TSESLint @typescript-eslint/utils
TSESTree @typescript-eslint/utils
AST_NODE_TYPES @typescript-eslint/utils
createRule ../util
getOperatorPrecedence ../util
getOperatorPrecedenceForNode ../util
getParserServices ../util
getTextWithParentheses ../util
isParenthesized ../util
getWrappedCode ../util/getWrappedCode

Variables & Constants

Name Type Kind Value Exported
messageId any const options.assertionStyle
text string const ${expressionCodeWrapped} as ${typeAnnotationCode}
suggestions TSESLint.ReportSuggestionArray<MessageIds> const []

Functions

isConst(node: TSESTree.TypeNode): boolean

Code
function isConst(node: TSESTree.TypeNode): boolean {
      if (node.type !== AST_NODE_TYPES.TSTypeReference) {
        return false;
      }

      return (
        node.typeName.type === AST_NODE_TYPES.Identifier &&
        node.typeName.name === 'const'
      );
    }
  • Parameters:
  • node: TSESTree.TypeNode
  • Return Type: boolean

reportIncorrectAssertionType(node: AsExpressionOrTypeAssertion): void

Code
function reportIncorrectAssertionType(
      node: AsExpressionOrTypeAssertion,
    ): void {
      const messageId = options.assertionStyle;

      // If this node is `as const`, then don't report an error.
      if (isConst(node.typeAnnotation) && messageId === 'never') {
        return;
      }
      context.report({
        node,
        messageId,
        data:
          messageId !== 'never'
            ? { cast: context.sourceCode.getText(node.typeAnnotation) }
            : {},
        fix:
          messageId === 'as'
            ? (fixer): TSESLint.RuleFix => {
                // lazily access parserServices to avoid crashing on non TS files (#9860)
                const tsNode = getParserServices(
                  context,
                  true,
                ).esTreeNodeToTSNodeMap.get(node as TSESTree.TSTypeAssertion);

                const expressionCode = context.sourceCode.getText(
                  node.expression,
                );
                const typeAnnotationCode = context.sourceCode.getText(
                  node.typeAnnotation,
                );

                const asPrecedence = getOperatorPrecedence(
                  ts.SyntaxKind.AsExpression,
                  ts.SyntaxKind.Unknown,
                );
                const parentPrecedence = getOperatorPrecedence(
                  tsNode.parent.kind,
                  ts.isBinaryExpression(tsNode.parent)
                    ? tsNode.parent.operatorToken.kind
                    : ts.SyntaxKind.Unknown,
                  ts.isNewExpression(tsNode.parent)
                    ? tsNode.parent.arguments != null &&
                        tsNode.parent.arguments.length > 0
                    : undefined,
                );

                const expressionPrecedence = getOperatorPrecedenceForNode(
                  node.expression,
                );

                const expressionCodeWrapped = getWrappedCode(
                  expressionCode,
                  expressionPrecedence,
                  asPrecedence,
                );

                const text = `${expressionCodeWrapped} as ${typeAnnotationCode}`;
                return fixer.replaceText(
                  node,
                  isParenthesized(node, context.sourceCode)
                    ? text
                    : getWrappedCode(text, asPrecedence, parentPrecedence),
                );
              }
            : undefined,
      });
    }
  • Parameters:
  • node: AsExpressionOrTypeAssertion
  • Return Type: void
  • Calls:
  • isConst
  • context.report
  • context.sourceCode.getText
  • getParserServices( context, true, ).esTreeNodeToTSNodeMap.get
  • getOperatorPrecedence (from ../util)
  • ts.isBinaryExpression
  • ts.isNewExpression
  • getOperatorPrecedenceForNode (from ../util)
  • getWrappedCode (from ../util/getWrappedCode)
  • fixer.replaceText
  • isParenthesized (from ../util)
  • Internal Comments:
    // If this node is `as const`, then don't report an error.
    // lazily access parserServices to avoid crashing on non TS files (#9860) (x2)
    

checkType(node: TSESTree.TypeNode): boolean

Code
function checkType(node: TSESTree.TypeNode): boolean {
      switch (node.type) {
        case AST_NODE_TYPES.TSAnyKeyword:
        case AST_NODE_TYPES.TSUnknownKeyword:
          return false;
        case AST_NODE_TYPES.TSTypeReference:
          return (
            // Ignore `as const` and `<const>`
            !isConst(node) ||
            // Allow qualified names which have dots between identifiers, `Foo.Bar`
            node.typeName.type === AST_NODE_TYPES.TSQualifiedName
          );

        default:
          return true;
      }
    }
  • Parameters:
  • node: TSESTree.TypeNode
  • Return Type: boolean
  • Calls:
  • isConst
  • Internal Comments:
    // Ignore `as const` and `<const>` (x2)
    // Allow qualified names which have dots between identifiers, `Foo.Bar` (x4)
    

getSuggestions(node: AsExpressionOrTypeAssertion, annotationMessageId: MessageIds, satisfiesMessageId: MessageIds): TSESLint.ReportSuggestionArray<MessageIds>

Code
function getSuggestions(
      node: AsExpressionOrTypeAssertion,
      annotationMessageId: MessageIds,
      satisfiesMessageId: MessageIds,
    ): TSESLint.ReportSuggestionArray<MessageIds> {
      const suggestions: TSESLint.ReportSuggestionArray<MessageIds> = [];
      if (
        node.parent.type === AST_NODE_TYPES.VariableDeclarator &&
        !node.parent.id.typeAnnotation
      ) {
        const { parent } = node;
        suggestions.push({
          messageId: annotationMessageId,
          data: { cast: context.sourceCode.getText(node.typeAnnotation) },
          fix: fixer => [
            fixer.insertTextAfter(
              parent.id,
              `: ${context.sourceCode.getText(node.typeAnnotation)}`,
            ),
            fixer.replaceText(
              node,
              getTextWithParentheses(context.sourceCode, node.expression),
            ),
          ],
        });
      }
      suggestions.push({
        messageId: satisfiesMessageId,
        data: { cast: context.sourceCode.getText(node.typeAnnotation) },
        fix: fixer => [
          fixer.replaceText(
            node,
            getTextWithParentheses(context.sourceCode, node.expression),
          ),
          fixer.insertTextAfter(
            node,
            ` satisfies ${context.sourceCode.getText(node.typeAnnotation)}`,
          ),
        ],
      });
      return suggestions;
    }
  • Parameters:
  • node: AsExpressionOrTypeAssertion
  • annotationMessageId: MessageIds
  • satisfiesMessageId: MessageIds
  • Return Type: TSESLint.ReportSuggestionArray<MessageIds>
  • Calls:
  • suggestions.push
  • context.sourceCode.getText
  • fixer.insertTextAfter
  • fixer.replaceText
  • getTextWithParentheses (from ../util)

fix(fixer: any): any[]

Code
fixer => [
            fixer.insertTextAfter(
              parent.id,
              `: ${context.sourceCode.getText(node.typeAnnotation)}`,
            ),
            fixer.replaceText(
              node,
              getTextWithParentheses(context.sourceCode, node.expression),
            ),
          ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

fix(fixer: any): any[]

Code
fixer => [
            fixer.insertTextAfter(
              parent.id,
              `: ${context.sourceCode.getText(node.typeAnnotation)}`,
            ),
            fixer.replaceText(
              node,
              getTextWithParentheses(context.sourceCode, node.expression),
            ),
          ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

fix(fixer: any): any[]

Code
fixer => [
          fixer.replaceText(
            node,
            getTextWithParentheses(context.sourceCode, node.expression),
          ),
          fixer.insertTextAfter(
            node,
            ` satisfies ${context.sourceCode.getText(node.typeAnnotation)}`,
          ),
        ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

fix(fixer: any): any[]

Code
fixer => [
          fixer.replaceText(
            node,
            getTextWithParentheses(context.sourceCode, node.expression),
          ),
          fixer.insertTextAfter(
            node,
            ` satisfies ${context.sourceCode.getText(node.typeAnnotation)}`,
          ),
        ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

isAsParameter(node: AsExpressionOrTypeAssertion): boolean

Code
function isAsParameter(node: AsExpressionOrTypeAssertion): boolean {
      return (
        node.parent.type === AST_NODE_TYPES.NewExpression ||
        node.parent.type === AST_NODE_TYPES.CallExpression ||
        node.parent.type === AST_NODE_TYPES.ThrowStatement ||
        node.parent.type === AST_NODE_TYPES.AssignmentPattern ||
        node.parent.type === AST_NODE_TYPES.JSXExpressionContainer ||
        (node.parent.type === AST_NODE_TYPES.TemplateLiteral &&
          node.parent.parent.type === AST_NODE_TYPES.TaggedTemplateExpression)
      );
    }
  • Parameters:
  • node: AsExpressionOrTypeAssertion
  • Return Type: boolean

checkExpressionForObjectAssertion(node: AsExpressionOrTypeAssertion): void

Code
function checkExpressionForObjectAssertion(
      node: AsExpressionOrTypeAssertion,
    ): void {
      if (
        options.assertionStyle === 'never' ||
        options.objectLiteralTypeAssertions === 'allow' ||
        node.expression.type !== AST_NODE_TYPES.ObjectExpression
      ) {
        return;
      }

      if (
        options.objectLiteralTypeAssertions === 'allow-as-parameter' &&
        isAsParameter(node)
      ) {
        return;
      }

      if (checkType(node.typeAnnotation)) {
        const suggest = getSuggestions(
          node,
          'replaceObjectTypeAssertionWithAnnotation',
          'replaceObjectTypeAssertionWithSatisfies',
        );

        context.report({
          node,
          messageId: 'unexpectedObjectTypeAssertion',
          suggest,
        });
      }
    }
  • Parameters:
  • node: AsExpressionOrTypeAssertion
  • Return Type: void
  • Calls:
  • isAsParameter
  • checkType
  • getSuggestions
  • context.report

checkExpressionForArrayAssertion(node: AsExpressionOrTypeAssertion): void

Code
function checkExpressionForArrayAssertion(
      node: AsExpressionOrTypeAssertion,
    ): void {
      if (
        options.assertionStyle === 'never' ||
        options.arrayLiteralTypeAssertions === 'allow' ||
        node.expression.type !== AST_NODE_TYPES.ArrayExpression
      ) {
        return;
      }

      if (
        options.arrayLiteralTypeAssertions === 'allow-as-parameter' &&
        isAsParameter(node)
      ) {
        return;
      }

      if (checkType(node.typeAnnotation)) {
        const suggest = getSuggestions(
          node,
          'replaceArrayTypeAssertionWithAnnotation',
          'replaceArrayTypeAssertionWithSatisfies',
        );

        context.report({
          node,
          messageId: 'unexpectedArrayTypeAssertion',
          suggest,
        });
      }
    }
  • Parameters:
  • node: AsExpressionOrTypeAssertion
  • Return Type: void
  • Calls:
  • isAsParameter
  • checkType
  • getSuggestions
  • context.report

fix(fixer: any): any[]

Code
fixer => [
            fixer.insertTextAfter(
              parent.id,
              `: ${context.sourceCode.getText(node.typeAnnotation)}`,
            ),
            fixer.replaceText(
              node,
              getTextWithParentheses(context.sourceCode, node.expression),
            ),
          ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

fix(fixer: any): any[]

Code
fixer => [
            fixer.insertTextAfter(
              parent.id,
              `: ${context.sourceCode.getText(node.typeAnnotation)}`,
            ),
            fixer.replaceText(
              node,
              getTextWithParentheses(context.sourceCode, node.expression),
            ),
          ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

fix(fixer: any): any[]

Code
fixer => [
          fixer.replaceText(
            node,
            getTextWithParentheses(context.sourceCode, node.expression),
          ),
          fixer.insertTextAfter(
            node,
            ` satisfies ${context.sourceCode.getText(node.typeAnnotation)}`,
          ),
        ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

fix(fixer: any): any[]

Code
fixer => [
          fixer.replaceText(
            node,
            getTextWithParentheses(context.sourceCode, node.expression),
          ),
          fixer.insertTextAfter(
            node,
            ` satisfies ${context.sourceCode.getText(node.typeAnnotation)}`,
          ),
        ]
  • Parameters:
  • fixer: any
  • Return Type: any[]

Type Aliases

MessageIds

type MessageIds = | 'angle-bracket'
  | 'as'
  | 'never'
  | 'replaceArrayTypeAssertionWithAnnotation'
  | 'replaceArrayTypeAssertionWithSatisfies'
  | 'replaceObjectTypeAssertionWithAnnotation'
  | 'replaceObjectTypeAssertionWithSatisfies'
  | 'unexpectedArrayTypeAssertion'
  | 'unexpectedObjectTypeAssertion';

OptUnion

type OptUnion = | {
      assertionStyle: 'angle-bracket' | 'as';
      objectLiteralTypeAssertions?: 'allow' | 'allow-as-parameter' | 'never';
      arrayLiteralTypeAssertions?: 'allow' | 'allow-as-parameter' | 'never';
    }
  | {
      assertionStyle: 'never';
    };

Options

type Options = readonly [OptUnion];

AsExpressionOrTypeAssertion

type AsExpressionOrTypeAssertion = | TSESTree.TSAsExpression
  | TSESTree.TSTypeAssertion;