Skip to content

⬅️ Back to Table of Contents

πŸ“„ no-unsafe-type-assertion

πŸ“Š Analysis Summary

Metric Count
πŸ”§ Functions 4
πŸ“¦ Imports 6

πŸ“š Table of Contents

πŸ› οΈ File Location:

πŸ“‚ packages/eslint-plugin/src/rules/no-unsafe-type-assertion.ts

πŸ“€ Default Export

export default createRule({ ... })
Property Value
name 'no-unsafe-type-assertion'
meta.type 'problem'
meta.docs.description 'Disallow type assertions that narrow a type'
meta.docs.requiresTypeChecking true
meta.messages.unsafeOfAnyTypeAssertion 'Unsafe assertion from {{type}} detected: consider using type guards or a safer assertion.'
meta.messages.unsafeToAnyTypeAssertion 'Unsafe assertion to {{type}} detected: consider using a more specific type to ensure safety.'
meta.messages.unsafeToUnconstrainedTypeAssertion "Unsafe type assertion: '{{type}}' could be instantiated with an arbitrary type which could be unrelated to the origi...
meta.messages.unsafeTypeAssertion "Unsafe type assertion: type '{{type}}' is more narrow than the original type."
meta.messages.unsafeTypeAssertionAssignableToConstraint "Unsafe type assertion: the original type is assignable to the constraint of type '{{type}}', but '{{type}}' could be...
meta.schema []
defaultOptions []

Entry point: create β€” documented under Functions.


πŸ“¦ Imports

Name Source
TSESTree @typescript-eslint/utils
createRule ../util
getParserServices ../util
isTypeAnyType ../util
isTypeUnknownType ../util
isUnsafeAssignment ../util

Functions

create(context: any): { 'TSAsExpression, TSTypeAssertion'(node: TSESTree.TSAsExpr…

Parameters:

  • context any

Returns: { 'TSAsExpression, TSTypeAssertion'(node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion): void; }

Calls:

  • getParserServices (from ../util)
  • services.program.getTypeChecker
  • tsutils.isIntrinsicErrorType
  • tsutils.isObjectType
  • tsutils.isObjectFlagSet
  • services.getTypeAtLocation
  • isTypeAnyType (from ../util)
  • isTypeUnknownType (from ../util)
  • context.report
  • isUnsafeAssignment (from ../util)
  • getAnyTypeName
  • isObjectLiteralType
  • checker.getWidenedType
  • checker.isTypeAssignableTo
  • tsutils.isTypeParameter
  • checker.getBaseConstraintOfType
  • checker.typeToString
  • checkExpression

Internal Comments:

// handle cases when asserting unknown ==> any.
// Use the widened type in case of an object literal so `isTypeAssignableTo()` (x2)
// won't fail on excess property check. (x2)
// workaround for https://github.com/microsoft/TypeScript/issues/62933
// Produce a more specific error message when targeting a type parameter
// asserting to an unconstrained type parameter is unsafe (x4)
// special case message if the original type is assignable to the (x2)
// constraint of the target type parameter (x2)
// General error message (x4)

Code
create(context) {
    const services = getParserServices(context);
    const checker = services.program.getTypeChecker();

    function getAnyTypeName(type: ts.Type): string {
      return tsutils.isIntrinsicErrorType(type) ? 'error typed' : '`any`';
    }

    function isObjectLiteralType(type: ts.Type): boolean {
      return (
        tsutils.isObjectType(type) &&
        tsutils.isObjectFlagSet(type, ts.ObjectFlags.ObjectLiteral)
      );
    }

    function checkExpression(
      node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion,
    ): void {
      const expressionType = services.getTypeAtLocation(node.expression);
      const assertedType = services.getTypeAtLocation(node.typeAnnotation);

      if (expressionType === assertedType) {
        return;
      }

      // handle cases when asserting unknown ==> any.
      if (isTypeAnyType(assertedType) && isTypeUnknownType(expressionType)) {
        context.report({
          node,
          messageId: 'unsafeToAnyTypeAssertion',
          data: {
            type: '`any`',
          },
        });

        return;
      }

      const unsafeExpressionAny = isUnsafeAssignment(
        expressionType,
        assertedType,
        checker,
        node.expression,
      );

      if (unsafeExpressionAny) {
        context.report({
          node,
          messageId: 'unsafeOfAnyTypeAssertion',
          data: {
            type: getAnyTypeName(unsafeExpressionAny.sender),
          },
        });

        return;
      }

      const unsafeAssertedAny = isUnsafeAssignment(
        assertedType,
        expressionType,
        checker,
        node.typeAnnotation,
      );

      if (unsafeAssertedAny) {
        context.report({
          node,
          messageId: 'unsafeToAnyTypeAssertion',
          data: {
            type: getAnyTypeName(unsafeAssertedAny.sender),
          },
        });

        return;
      }

      // Use the widened type in case of an object literal so `isTypeAssignableTo()`
      // won't fail on excess property check.
      const expressionWidenedType = isObjectLiteralType(expressionType)
        ? checker.getWidenedType(expressionType)
        : expressionType;

      let isAssertionSafe: boolean;
      try {
        isAssertionSafe = checker.isTypeAssignableTo(
          expressionWidenedType,
          assertedType,
        );
      } catch {
        // workaround for https://github.com/microsoft/TypeScript/issues/62933
        return;
      }
      if (isAssertionSafe) {
        return;
      }

      // Produce a more specific error message when targeting a type parameter
      if (tsutils.isTypeParameter(assertedType)) {
        const assertedTypeConstraint =
          checker.getBaseConstraintOfType(assertedType);
        if (!assertedTypeConstraint) {
          // asserting to an unconstrained type parameter is unsafe
          context.report({
            node,
            messageId: 'unsafeToUnconstrainedTypeAssertion',
            data: {
              type: checker.typeToString(assertedType),
            },
          });
          return;
        }

        // special case message if the original type is assignable to the
        // constraint of the target type parameter
        const isAssignableToConstraint = checker.isTypeAssignableTo(
          expressionWidenedType,
          assertedTypeConstraint,
        );
        if (isAssignableToConstraint) {
          context.report({
            node,
            messageId: 'unsafeTypeAssertionAssignableToConstraint',
            data: {
              type: checker.typeToString(assertedType),
            },
          });
          return;
        }
      }

      // General error message
      context.report({
        node,
        messageId: 'unsafeTypeAssertion',
        data: {
          type: checker.typeToString(assertedType),
        },
      });
    }

    return {
      'TSAsExpression, TSTypeAssertion'(
        node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion,
      ): void {
        checkExpression(node);
      },
    };
  }

Internal helpers

Declared inside another function in this file.

getAnyTypeName(type: ts.Type): string

Parameters:

  • type ts.Type

Returns: string

Calls:

  • tsutils.isIntrinsicErrorType
Code
function getAnyTypeName(type: ts.Type): string {
      return tsutils.isIntrinsicErrorType(type) ? 'error typed' : '`any`';
    }

isObjectLiteralType(type: ts.Type): boolean

Parameters:

  • type ts.Type

Returns: boolean

Calls:

  • tsutils.isObjectType
  • tsutils.isObjectFlagSet
Code
function isObjectLiteralType(type: ts.Type): boolean {
      return (
        tsutils.isObjectType(type) &&
        tsutils.isObjectFlagSet(type, ts.ObjectFlags.ObjectLiteral)
      );
    }

checkExpression(node: TSESTree.TSAsExpression | TSESTree.TSTy…): void

Parameters:

  • node TSESTree.TSAsExpression | TSESTree.TSTypeAssertion

Returns: void

Calls:

  • services.getTypeAtLocation
  • isTypeAnyType (from ../util)
  • isTypeUnknownType (from ../util)
  • context.report
  • isUnsafeAssignment (from ../util)
  • getAnyTypeName
  • isObjectLiteralType
  • checker.getWidenedType
  • checker.isTypeAssignableTo
  • tsutils.isTypeParameter
  • checker.getBaseConstraintOfType
  • checker.typeToString

Internal Comments:

// handle cases when asserting unknown ==> any.
// Use the widened type in case of an object literal so `isTypeAssignableTo()` (x2)
// won't fail on excess property check. (x2)
// workaround for https://github.com/microsoft/TypeScript/issues/62933
// Produce a more specific error message when targeting a type parameter
// asserting to an unconstrained type parameter is unsafe (x4)
// special case message if the original type is assignable to the (x2)
// constraint of the target type parameter (x2)
// General error message (x4)

Code
function checkExpression(
      node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion,
    ): void {
      const expressionType = services.getTypeAtLocation(node.expression);
      const assertedType = services.getTypeAtLocation(node.typeAnnotation);

      if (expressionType === assertedType) {
        return;
      }

      // handle cases when asserting unknown ==> any.
      if (isTypeAnyType(assertedType) && isTypeUnknownType(expressionType)) {
        context.report({
          node,
          messageId: 'unsafeToAnyTypeAssertion',
          data: {
            type: '`any`',
          },
        });

        return;
      }

      const unsafeExpressionAny = isUnsafeAssignment(
        expressionType,
        assertedType,
        checker,
        node.expression,
      );

      if (unsafeExpressionAny) {
        context.report({
          node,
          messageId: 'unsafeOfAnyTypeAssertion',
          data: {
            type: getAnyTypeName(unsafeExpressionAny.sender),
          },
        });

        return;
      }

      const unsafeAssertedAny = isUnsafeAssignment(
        assertedType,
        expressionType,
        checker,
        node.typeAnnotation,
      );

      if (unsafeAssertedAny) {
        context.report({
          node,
          messageId: 'unsafeToAnyTypeAssertion',
          data: {
            type: getAnyTypeName(unsafeAssertedAny.sender),
          },
        });

        return;
      }

      // Use the widened type in case of an object literal so `isTypeAssignableTo()`
      // won't fail on excess property check.
      const expressionWidenedType = isObjectLiteralType(expressionType)
        ? checker.getWidenedType(expressionType)
        : expressionType;

      let isAssertionSafe: boolean;
      try {
        isAssertionSafe = checker.isTypeAssignableTo(
          expressionWidenedType,
          assertedType,
        );
      } catch {
        // workaround for https://github.com/microsoft/TypeScript/issues/62933
        return;
      }
      if (isAssertionSafe) {
        return;
      }

      // Produce a more specific error message when targeting a type parameter
      if (tsutils.isTypeParameter(assertedType)) {
        const assertedTypeConstraint =
          checker.getBaseConstraintOfType(assertedType);
        if (!assertedTypeConstraint) {
          // asserting to an unconstrained type parameter is unsafe
          context.report({
            node,
            messageId: 'unsafeToUnconstrainedTypeAssertion',
            data: {
              type: checker.typeToString(assertedType),
            },
          });
          return;
        }

        // special case message if the original type is assignable to the
        // constraint of the target type parameter
        const isAssignableToConstraint = checker.isTypeAssignableTo(
          expressionWidenedType,
          assertedTypeConstraint,
        );
        if (isAssignableToConstraint) {
          context.report({
            node,
            messageId: 'unsafeTypeAssertionAssignableToConstraint',
            data: {
              type: checker.typeToString(assertedType),
            },
          });
          return;
        }
      }

      // General error message
      context.report({
        node,
        messageId: 'unsafeTypeAssertion',
        data: {
          type: checker.typeToString(assertedType),
        },
      });
    }

Generated by Syntax Scribe