Skip to content

⬅️ Back to Table of Contents

πŸ“„ non-nullable-type-assertion-style

πŸ“Š Analysis Summary

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

πŸ“š Table of Contents

πŸ› οΈ File Location:

πŸ“‚ packages/eslint-plugin/src/rules/non-nullable-type-assertion-style.ts

πŸ“€ Default Export

export default createRule({ ... })
Property Value
name 'non-nullable-type-assertion-style'
meta.type 'suggestion'
meta.docs.description 'Enforce non-null assertions over explicit type assertions'
meta.docs.recommended 'stylistic'
meta.docs.requiresTypeChecking true
meta.fixable 'code'
meta.messages.preferNonNullAssertion 'Use a ! assertion to more succinctly remove null and undefined from the type.'
meta.schema []
defaultOptions []

Entry point: create β€” documented under Functions.


πŸ“¦ Imports

Name Source
TSESTree @typescript-eslint/utils
AST_NODE_TYPES @typescript-eslint/utils
createRule ../util
getOperatorPrecedence ../util
getParserServices ../util
OperatorPrecedence ../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.getTypeAtLocation
  • tsutils.isTypeFlagSet
  • tsutils.unionConstituents
  • type.getConstraint
  • couldBeNullish
  • tsutils.isUnionType
  • originalTypes.filter
  • nonNullishOriginalTypes.includes
  • assertedTypes.includes
  • isConstAssertion
  • getTypesIfNotLoose
  • sameTypeWithoutNullish
  • context.sourceCode.getText
  • getOperatorPrecedence (from ../util)
  • services.esTreeNodeToTSNodeMap.get
  • context.report
  • fixer.replaceText
Code
create(context) {
    const services = getParserServices(context);

    const getTypesIfNotLoose = (node: TSESTree.Node): ts.Type[] | undefined => {
      const type = services.getTypeAtLocation(node);

      if (
        tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)
      ) {
        return undefined;
      }

      return tsutils.unionConstituents(type);
    };

    const couldBeNullish = (type: ts.Type): boolean => {
      if (tsutils.isTypeFlagSet(type, ts.TypeFlags.TypeParameter)) {
        const constraint = type.getConstraint();
        return constraint == null || couldBeNullish(constraint);
      }

      if (tsutils.isUnionType(type)) {
        for (const part of type.types) {
          if (couldBeNullish(part)) {
            return true;
          }
        }
        return false;
      }
      return tsutils.isTypeFlagSet(
        type,
        ts.TypeFlags.Null | ts.TypeFlags.Undefined,
      );
    };

    const sameTypeWithoutNullish = (
      assertedTypes: ts.Type[],
      originalTypes: ts.Type[],
    ): boolean => {
      const nonNullishOriginalTypes = originalTypes.filter(
        type =>
          !tsutils.isTypeFlagSet(
            type,
            ts.TypeFlags.Null | ts.TypeFlags.Undefined,
          ),
      );

      if (nonNullishOriginalTypes.length === originalTypes.length) {
        return false;
      }

      for (const assertedType of assertedTypes) {
        if (
          couldBeNullish(assertedType) ||
          !nonNullishOriginalTypes.includes(assertedType)
        ) {
          return false;
        }
      }

      for (const originalType of nonNullishOriginalTypes) {
        if (!assertedTypes.includes(originalType)) {
          return false;
        }
      }

      return true;
    };

    const isConstAssertion = (
      node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion,
    ): boolean => {
      return (
        node.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&
        node.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier &&
        node.typeAnnotation.typeName.name === 'const'
      );
    };

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

        const originalTypes = getTypesIfNotLoose(node.expression);
        if (!originalTypes) {
          return;
        }

        const assertedTypes = getTypesIfNotLoose(node.typeAnnotation);
        if (!assertedTypes) {
          return;
        }

        if (sameTypeWithoutNullish(assertedTypes, originalTypes)) {
          const expressionSourceCode = context.sourceCode.getText(
            node.expression,
          );

          const higherPrecedenceThanUnary =
            getOperatorPrecedence(
              services.esTreeNodeToTSNodeMap.get(node.expression).kind,
              ts.SyntaxKind.Unknown,
            ) > OperatorPrecedence.Unary;

          context.report({
            node,
            messageId: 'preferNonNullAssertion',
            fix(fixer) {
              return fixer.replaceText(
                node,
                higherPrecedenceThanUnary
                  ? `${expressionSourceCode}!`
                  : `(${expressionSourceCode})!`,
              );
            },
          });
        }
      },
    };
  }

Internal helpers

Declared inside another function in this file.

getTypesIfNotLoose(node: TSESTree.Node): ts.Type[] | undefined

Parameters:

  • node TSESTree.Node

Returns: ts.Type[] | undefined

Calls:

  • services.getTypeAtLocation
  • tsutils.isTypeFlagSet
  • tsutils.unionConstituents
Code
(node: TSESTree.Node): ts.Type[] | undefined => {
      const type = services.getTypeAtLocation(node);

      if (
        tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)
      ) {
        return undefined;
      }

      return tsutils.unionConstituents(type);
    }

couldBeNullish(type: ts.Type): boolean

Parameters:

  • type ts.Type

Returns: boolean

Calls:

  • tsutils.isTypeFlagSet
  • type.getConstraint
  • couldBeNullish
  • tsutils.isUnionType
Code
(type: ts.Type): boolean => {
      if (tsutils.isTypeFlagSet(type, ts.TypeFlags.TypeParameter)) {
        const constraint = type.getConstraint();
        return constraint == null || couldBeNullish(constraint);
      }

      if (tsutils.isUnionType(type)) {
        for (const part of type.types) {
          if (couldBeNullish(part)) {
            return true;
          }
        }
        return false;
      }
      return tsutils.isTypeFlagSet(
        type,
        ts.TypeFlags.Null | ts.TypeFlags.Undefined,
      );
    }

sameTypeWithoutNullish(assertedTypes: ts.Type[], originalTypes: ts.Type[]): boolean

Parameters:

  • assertedTypes ts.Type[]
  • originalTypes ts.Type[]

Returns: boolean

Calls:

  • originalTypes.filter
  • tsutils.isTypeFlagSet
  • couldBeNullish
  • nonNullishOriginalTypes.includes
  • assertedTypes.includes
Code
(
      assertedTypes: ts.Type[],
      originalTypes: ts.Type[],
    ): boolean => {
      const nonNullishOriginalTypes = originalTypes.filter(
        type =>
          !tsutils.isTypeFlagSet(
            type,
            ts.TypeFlags.Null | ts.TypeFlags.Undefined,
          ),
      );

      if (nonNullishOriginalTypes.length === originalTypes.length) {
        return false;
      }

      for (const assertedType of assertedTypes) {
        if (
          couldBeNullish(assertedType) ||
          !nonNullishOriginalTypes.includes(assertedType)
        ) {
          return false;
        }
      }

      for (const originalType of nonNullishOriginalTypes) {
        if (!assertedTypes.includes(originalType)) {
          return false;
        }
      }

      return true;
    }

isConstAssertion(node: TSESTree.TSAsExpression | TSESTree.TSTy…): boolean

Parameters:

  • node TSESTree.TSAsExpression | TSESTree.TSTypeAssertion

Returns: boolean

Code
(
      node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion,
    ): boolean => {
      return (
        node.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&
        node.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier &&
        node.typeAnnotation.typeName.name === 'const'
      );
    }

Generated by Syntax Scribe