Skip to content

⬅️ Back to Table of Contents

📄 no-inferrable-types.ts

📊 Analysis Summary

Metric Count
🔧 Functions 9
📦 Imports 6
📊 Variables & Constants 6
📑 Type Aliases 3

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin/src/rules/no-inferrable-types.ts

📦 Imports

Name Source
TSESTree @typescript-eslint/utils
AST_NODE_TYPES @typescript-eslint/utils
createRule ../util
nullThrows ../util
NullThrowsReasons ../util
skipChainExpression ../util

Variables & Constants

Name Type Kind Value Exported
keywordMap { [x: number]: string; } const `{
[AST_NODE_TYPES.TSBigIntKeyword]: 'bigint',
[AST_NODE_TYPES.TSBooleanKeyword]: 'boolean',
[AST_NODE_TYPES.TSNullKeyword]: 'null',
[AST_NODE_TYPES.TSNumberKeyword]: 'number',
[AST_NODE_TYPES.TSStringKeyword]: 'string',
[AST_NODE_TYPES.TSSymbolKeyword]: 'symbol',
[AST_NODE_TYPES.TSUndefinedKeyword]: 'undefined',
}`
unwrappedInit any const `hasUnaryPrefix(init, '-')
? init.argument
: init`
unwrappedInit any const `hasUnaryPrefix(init, '+', '-')
? init.argument
: init`
isRegExpLiteral boolean const `init.type === AST_NODE_TYPES.Literal &&
init.value instanceof RegExp`
isRegExpNewCall boolean const `init.type === AST_NODE_TYPES.NewExpression &&
init.callee.type === AST_NODE_TYPES.Identifier &&
init.callee.name === 'RegExp'`
type string const `typeNode.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference
? // TODO - if we add more references
'RegExp'
: keywordMap[typeNode.typeAnnotation.type]`

Functions

isFunctionCall(init: TSESTree.Expression, callName: string): boolean

Code
function isFunctionCall(
      init: TSESTree.Expression,
      callName: string,
    ): boolean {
      const node = skipChainExpression(init);

      return (
        node.type === AST_NODE_TYPES.CallExpression &&
        node.callee.type === AST_NODE_TYPES.Identifier &&
        node.callee.name === callName
      );
    }
  • Parameters:
  • init: TSESTree.Expression
  • callName: string
  • Return Type: boolean
  • Calls:
  • skipChainExpression (from ../util)

isLiteral(init: TSESTree.Expression, typeName: string): boolean

Code
function isLiteral(init: TSESTree.Expression, typeName: string): boolean {
      return (
        init.type === AST_NODE_TYPES.Literal && typeof init.value === typeName
      );
    }
  • Parameters:
  • init: TSESTree.Expression
  • typeName: string
  • Return Type: boolean

isIdentifier(init: TSESTree.Expression, names: string[]): boolean

Code
function isIdentifier(
      init: TSESTree.Expression,
      ...names: string[]
    ): boolean {
      return (
        init.type === AST_NODE_TYPES.Identifier && names.includes(init.name)
      );
    }
  • Parameters:
  • init: TSESTree.Expression
  • names: string[]
  • Return Type: boolean
  • Calls:
  • names.includes

hasUnaryPrefix(init: TSESTree.Expression, operators: string[]): init is TSESTree.UnaryExpression

Code
function hasUnaryPrefix(
      init: TSESTree.Expression,
      ...operators: string[]
    ): init is TSESTree.UnaryExpression {
      return (
        init.type === AST_NODE_TYPES.UnaryExpression &&
        operators.includes(init.operator)
      );
    }
  • Parameters:
  • init: TSESTree.Expression
  • operators: string[]
  • Return Type: init is TSESTree.UnaryExpression
  • Calls:
  • operators.includes

isInferrable(annotation: TSESTree.TypeNode, init: TSESTree.Expression): annotation is Keywords

Code
function isInferrable(
      annotation: TSESTree.TypeNode,
      init: TSESTree.Expression,
    ): annotation is Keywords {
      switch (annotation.type) {
        case AST_NODE_TYPES.TSBigIntKeyword: {
          // note that bigint cannot have + prefixed to it
          const unwrappedInit = hasUnaryPrefix(init, '-')
            ? init.argument
            : init;

          return (
            isFunctionCall(unwrappedInit, 'BigInt') ||
            unwrappedInit.type === AST_NODE_TYPES.Literal
          );
        }

        case AST_NODE_TYPES.TSBooleanKeyword:
          return (
            hasUnaryPrefix(init, '!') ||
            isFunctionCall(init, 'Boolean') ||
            isLiteral(init, 'boolean')
          );

        case AST_NODE_TYPES.TSNumberKeyword: {
          const unwrappedInit = hasUnaryPrefix(init, '+', '-')
            ? init.argument
            : init;

          return (
            isIdentifier(unwrappedInit, 'Infinity', 'NaN') ||
            isFunctionCall(unwrappedInit, 'Number') ||
            isLiteral(unwrappedInit, 'number')
          );
        }

        case AST_NODE_TYPES.TSNullKeyword:
          return init.type === AST_NODE_TYPES.Literal && init.value == null;

        case AST_NODE_TYPES.TSStringKeyword:
          return (
            isFunctionCall(init, 'String') ||
            isLiteral(init, 'string') ||
            init.type === AST_NODE_TYPES.TemplateLiteral
          );

        case AST_NODE_TYPES.TSSymbolKeyword:
          return isFunctionCall(init, 'Symbol');

        case AST_NODE_TYPES.TSTypeReference: {
          if (
            annotation.typeName.type === AST_NODE_TYPES.Identifier &&
            annotation.typeName.name === 'RegExp'
          ) {
            const isRegExpLiteral =
              init.type === AST_NODE_TYPES.Literal &&
              init.value instanceof RegExp;
            const isRegExpNewCall =
              init.type === AST_NODE_TYPES.NewExpression &&
              init.callee.type === AST_NODE_TYPES.Identifier &&
              init.callee.name === 'RegExp';
            const isRegExpCall = isFunctionCall(init, 'RegExp');

            return isRegExpLiteral || isRegExpCall || isRegExpNewCall;
          }

          return false;
        }

        case AST_NODE_TYPES.TSUndefinedKeyword:
          return (
            hasUnaryPrefix(init, 'void') || isIdentifier(init, 'undefined')
          );
      }

      return false;
    }
  • JSDoc:

    /**
         * Returns whether a node has an inferrable value or not
         */
    

  • Parameters:

  • annotation: TSESTree.TypeNode
  • init: TSESTree.Expression
  • Return Type: annotation is Keywords
  • Calls:
  • hasUnaryPrefix
  • isFunctionCall
  • isLiteral
  • isIdentifier
  • Internal Comments:
    // note that bigint cannot have + prefixed to it (x2)
    

`reportInferrableType(node: | TSESTree.AccessorProperty

    | TSESTree.Parameter
    | TSESTree.PropertyDefinition
    | TSESTree.VariableDeclarator, typeNode: TSESTree.TSTypeAnnotation | undefined, initNode: TSESTree.Expression | null | undefined): void`
Code
function reportInferrableType(
      node:
        | TSESTree.AccessorProperty
        | TSESTree.Parameter
        | TSESTree.PropertyDefinition
        | TSESTree.VariableDeclarator,
      typeNode: TSESTree.TSTypeAnnotation | undefined,
      initNode: TSESTree.Expression | null | undefined,
    ): void {
      if (!typeNode || !initNode) {
        return;
      }

      if (!isInferrable(typeNode.typeAnnotation, initNode)) {
        return;
      }

      const type =
        typeNode.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference
          ? // TODO - if we add more references
            'RegExp'
          : keywordMap[typeNode.typeAnnotation.type];

      context.report({
        node,
        messageId: 'noInferrableType',
        data: {
          type,
        },
        *fix(fixer) {
          if (
            (node.type === AST_NODE_TYPES.AssignmentPattern &&
              node.left.optional) ||
            (node.type === AST_NODE_TYPES.PropertyDefinition && node.definite)
          ) {
            yield fixer.remove(
              nullThrows(
                context.sourceCode.getTokenBefore(typeNode),
                NullThrowsReasons.MissingToken('token before', 'type node'),
              ),
            );
          }
          yield fixer.remove(typeNode);
        },
      });
    }
  • JSDoc:

    /**
         * Reports an inferrable type declaration, if any
         */
    

  • Parameters:

  • node: | TSESTree.AccessorProperty | TSESTree.Parameter | TSESTree.PropertyDefinition | TSESTree.VariableDeclarator
  • typeNode: TSESTree.TSTypeAnnotation | undefined
  • initNode: TSESTree.Expression | null | undefined
  • Return Type: void
  • Calls:
  • isInferrable
  • context.report
  • fixer.remove
  • nullThrows (from ../util)
  • context.sourceCode.getTokenBefore
  • NullThrowsReasons.MissingToken

inferrableVariableVisitor(node: TSESTree.VariableDeclarator): void

Code
function inferrableVariableVisitor(
      node: TSESTree.VariableDeclarator,
    ): void {
      reportInferrableType(node, node.id.typeAnnotation, node.init);
    }
  • Parameters:
  • node: TSESTree.VariableDeclarator
  • Return Type: void
  • Calls:
  • reportInferrableType

`inferrableParameterVisitor(node: | TSESTree.ArrowFunctionExpression

    | TSESTree.FunctionDeclaration
    | TSESTree.FunctionExpression): void`
Code
function inferrableParameterVisitor(
      node:
        | TSESTree.ArrowFunctionExpression
        | TSESTree.FunctionDeclaration
        | TSESTree.FunctionExpression,
    ): void {
      if (ignoreParameters) {
        return;
      }

      node.params.forEach(param => {
        if (param.type === AST_NODE_TYPES.TSParameterProperty) {
          param = param.parameter;
        }

        if (param.type === AST_NODE_TYPES.AssignmentPattern) {
          reportInferrableType(param, param.left.typeAnnotation, param.right);
        }
      });
    }
  • Parameters:
  • node: | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression
  • Return Type: void
  • Calls:
  • node.params.forEach
  • reportInferrableType

inferrablePropertyVisitor(node: TSESTree.AccessorProperty | TSESTree.PropertyDefinition): void

Code
function inferrablePropertyVisitor(
      node: TSESTree.AccessorProperty | TSESTree.PropertyDefinition,
    ): void {
      // We ignore `readonly` because of Microsoft/TypeScript#14416
      // Essentially a readonly property without a type
      // will result in its value being the type, leading to
      // compile errors if the type is stripped.
      if (ignoreProperties || node.readonly || node.optional) {
        return;
      }
      reportInferrableType(node, node.typeAnnotation, node.value);
    }
  • Parameters:
  • node: TSESTree.AccessorProperty | TSESTree.PropertyDefinition
  • Return Type: void
  • Calls:
  • reportInferrableType
  • Internal Comments:
    // We ignore `readonly` because of Microsoft/TypeScript#14416
    // Essentially a readonly property without a type
    // will result in its value being the type, leading to
    // compile errors if the type is stripped.
    

Type Aliases

Options

type Options = [
  {
    ignoreParameters?: boolean;
    ignoreProperties?: boolean;
  },
];

MessageIds

type MessageIds = 'noInferrableType';

Keywords

type Keywords = | TSESTree.TSBigIntKeyword
      | TSESTree.TSBooleanKeyword
      | TSESTree.TSNullKeyword
      | TSESTree.TSNumberKeyword
      | TSESTree.TSStringKeyword
      | TSESTree.TSSymbolKeyword
      | TSESTree.TSTypeReference
      | TSESTree.TSUndefinedKeyword;