Skip to content

⬅️ Back to Table of Contents

πŸ“„ no-magic-numbers

πŸ“Š Analysis Summary

Metric Count
πŸ”§ Functions 11
πŸ“¦ Imports 8
πŸ“Š Variables & Constants 2
πŸ“‘ Type Aliases 2

πŸ“š Table of Contents

πŸ› οΈ File Location:

πŸ“‚ packages/eslint-plugin/src/rules/no-magic-numbers.ts

πŸ“€ Default Export

export default createRule<Options, MessageIds>({ ... })
Property Value
name 'no-magic-numbers'
meta.type 'suggestion'
meta.docs.description 'Disallow magic numbers'
meta.docs.extendsBaseRule true
meta.docs.frozen true
meta.messages baseRule.meta.messages
meta.schema [schema]

Entry point: create β€” documented under Functions.


πŸ“¦ Imports

Name Source
TSESTree @typescript-eslint/utils
JSONSchema4 @typescript-eslint/utils/json-schema
AST_NODE_TYPES @typescript-eslint/utils
InferMessageIdsTypeFromRule ../util
InferOptionsTypeFromRule ../util
createRule ../util
deepMerge ../util
getESLintCoreRule ../util/getESLintCoreRule

Variables & Constants

Name Type Kind Value Exported
schema JSONSchema4 const deepMerge( // eslint-disable-next-line @typescript-eslint/no-unsafe-argument ... βœ—
defaultOptions Options const [ { detectObjects: false, enforceConst: false, ignore: [], ignoreArrayIndexes... βœ—

Functions

create(context: any, [options]: any): { Literal(node: any): void; }

Parameters:

  • context any
  • [options] any

Returns: { Literal(node: any): void; }

Calls:

  • baseRule.create
  • (options.ignore ?? []).map
  • ignored.has
  • normalizeLiteralValue
  • isParentTSEnumDeclaration
  • isTSNumericLiteralType
  • isAncestorTSIndexedAccessType
  • isParentTSReadonlyPropertyDefinition
  • context.report
  • rules.Literal

Internal Comments:

// If it’s not a numeric literal we’re not interested
// This will be `true` if we’re configured to ignore this case (eg. it’s (x2)
// an enum and `ignoreEnums` is `true`). It will be `false` if we’re not (x2)
// configured to ignore this case. It will remain `undefined` if this is (x2)
// not one of our exception cases, and we’ll fall back to the base rule. (x2)
// Check if the node is ignored
// If we’ve hit a case where the ignore option is true we can return now
// If the ignore option is *not* set we can report it now
// the base rule only shows the operator for negative numbers (x4)
// https://github.com/eslint/eslint/blob/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d/lib/rules/no-magic-numbers.js#L126 (x4)
// Let the base rule deal with the rest (x4)

Code
create(context, [options]) {
    const rules = baseRule.create(context);

    const ignored = new Set((options.ignore ?? []).map(normalizeIgnoreValue));

    return {
      Literal(node): void {
        // If it’s not a numeric literal we’re not interested
        if (typeof node.value !== 'number' && typeof node.value !== 'bigint') {
          return;
        }

        // This will be `true` if we’re configured to ignore this case (eg. it’s
        // an enum and `ignoreEnums` is `true`). It will be `false` if we’re not
        // configured to ignore this case. It will remain `undefined` if this is
        // not one of our exception cases, and we’ll fall back to the base rule.
        let isAllowed: boolean | undefined;

        // Check if the node is ignored
        if (ignored.has(normalizeLiteralValue(node, node.value))) {
          isAllowed = true;
        }
        // Check if the node is a TypeScript enum declaration
        else if (isParentTSEnumDeclaration(node)) {
          isAllowed = options.ignoreEnums === true;
        }
        // Check TypeScript specific nodes for Numeric Literal
        else if (isTSNumericLiteralType(node)) {
          isAllowed = options.ignoreNumericLiteralTypes === true;
        }
        // Check if the node is a type index
        else if (isAncestorTSIndexedAccessType(node)) {
          isAllowed = options.ignoreTypeIndexes === true;
        }
        // Check if the node is a readonly class property
        else if (isParentTSReadonlyPropertyDefinition(node)) {
          isAllowed = options.ignoreReadonlyClassProperties === true;
        }

        // If we’ve hit a case where the ignore option is true we can return now
        if (isAllowed === true) {
          return;
        }
        // If the ignore option is *not* set we can report it now
        if (isAllowed === false) {
          let fullNumberNode: TSESTree.Literal | TSESTree.UnaryExpression =
            node;
          let raw = node.raw;
          if (
            node.parent.type === AST_NODE_TYPES.UnaryExpression &&
            // the base rule only shows the operator for negative numbers
            // https://github.com/eslint/eslint/blob/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d/lib/rules/no-magic-numbers.js#L126
            node.parent.operator === '-'
          ) {
            fullNumberNode = node.parent;
            raw = `${node.parent.operator}${node.raw}`;
          }
          context.report({
            node: fullNumberNode,
            messageId: 'noMagic',
            data: { raw },
          });

          return;
        }

        // Let the base rule deal with the rest
        rules.Literal(node);
      },
    };
  }

normalizeIgnoreValue(value: bigint | number | string): bigint | number

Convert the value to bigint if it's a string. Otherwise, return the value as-is.

Parameters:

  • value any: The value to normalize.

Returns: undefined The normalized value.

Raw JSDoc
/**
 * Convert the value to bigint if it's a string. Otherwise, return the value as-is.
 * @param value The value to normalize.
 * @returns The normalized value.
 */

Calls:

  • BigInt
  • value.slice
Code
function normalizeIgnoreValue(
  value: bigint | number | string,
): bigint | number {
  if (typeof value === 'string') {
    return BigInt(value.slice(0, -1));
  }

  return value;
}

normalizeLiteralValue(node: TSESTree.BigIntLiteral | TSESTree.Numbe…, value: bigint | number): bigint | number

Converts the node to its numeric value, handling prefixed numbers (-1 / +1)

Parameters:

  • node any: the node to normalize.
  • value any: the node's value.
Raw JSDoc
/**
 * Converts the node to its numeric value, handling prefixed numbers (-1 / +1)
 * @param node the node to normalize.
 * @param value the node's value.
 */

Calls:

  • ['-', '+'].includes
Code
function normalizeLiteralValue(
  node: TSESTree.BigIntLiteral | TSESTree.NumberLiteral,
  value: bigint | number,
): bigint | number {
  if (
    node.parent.type === AST_NODE_TYPES.UnaryExpression &&
    ['-', '+'].includes(node.parent.operator) &&
    node.parent.operator === '-'
  ) {
    return -value;
  }

  return value;
}

getLiteralParent(node: TSESTree.Literal): TSESTree.Node | undefined

Gets the true parent of the literal, handling prefixed numbers (-1 / +1)

Raw JSDoc
/**
 * Gets the true parent of the literal, handling prefixed numbers (-1 / +1)
 */

Calls:

  • ['-', '+'].includes
Code
function getLiteralParent(node: TSESTree.Literal): TSESTree.Node | undefined {
  if (
    node.parent.type === AST_NODE_TYPES.UnaryExpression &&
    ['-', '+'].includes(node.parent.operator)
  ) {
    return node.parent.parent;
  }

  return node.parent;
}

isGrandparentTSTypeAliasDeclaration(node: TSESTree.Node): boolean

Checks if the node grandparent is a Typescript type alias declaration

Parameters:

  • node any: the node to be validated.

Returns: undefined true if the node grandparent is a Typescript type alias declaration

Tags: @private

Raw JSDoc
/**
 * Checks if the node grandparent is a Typescript type alias declaration
 * @param node the node to be validated.
 * @returns true if the node grandparent is a Typescript type alias declaration
 * @private
 */
Code
function isGrandparentTSTypeAliasDeclaration(node: TSESTree.Node): boolean {
  return node.parent?.parent?.type === AST_NODE_TYPES.TSTypeAliasDeclaration;
}

isGrandparentTSUnionType(node: TSESTree.Node): boolean

Checks if the node grandparent is a Typescript union type and its parent is a type alias declaration

Parameters:

  • node any: the node to be validated.

Returns: undefined true if the node grandparent is a Typescript union type and its parent is a type alias declaration

Tags: @private

Raw JSDoc
/**
 * Checks if the node grandparent is a Typescript union type and its parent is a type alias declaration
 * @param node the node to be validated.
 * @returns true if the node grandparent is a Typescript union type and its parent is a type alias declaration
 * @private
 */

Calls:

  • isGrandparentTSTypeAliasDeclaration
Code
function isGrandparentTSUnionType(node: TSESTree.Node): boolean {
  if (node.parent?.parent?.type === AST_NODE_TYPES.TSUnionType) {
    return isGrandparentTSTypeAliasDeclaration(node.parent);
  }

  return false;
}

isParentTSEnumDeclaration(node: TSESTree.Literal): boolean

Checks if the node parent is a Typescript enum member

Parameters:

  • node any: the node to be validated.

Returns: undefined true if the node parent is a Typescript enum member

Tags: @private

Raw JSDoc
/**
 * Checks if the node parent is a Typescript enum member
 * @param node the node to be validated.
 * @returns true if the node parent is a Typescript enum member
 * @private
 */

Calls:

  • getLiteralParent
Code
function isParentTSEnumDeclaration(node: TSESTree.Literal): boolean {
  const parent = getLiteralParent(node);
  return parent?.type === AST_NODE_TYPES.TSEnumMember;
}

isParentTSLiteralType(node: TSESTree.Node): boolean

Checks if the node parent is a Typescript literal type

Parameters:

  • node any: the node to be validated.

Returns: undefined true if the node parent is a Typescript literal type

Tags: @private

Raw JSDoc
/**
 * Checks if the node parent is a Typescript literal type
 * @param node the node to be validated.
 * @returns true if the node parent is a Typescript literal type
 * @private
 */
Code
function isParentTSLiteralType(node: TSESTree.Node): boolean {
  return node.parent?.type === AST_NODE_TYPES.TSLiteralType;
}

isTSNumericLiteralType(node: TSESTree.Node): boolean

Checks if the node is a valid TypeScript numeric literal type.

Parameters:

  • node any: the node to be validated.

Returns: undefined true if the node is a TypeScript numeric literal type.

Tags: @private

Raw JSDoc
/**
 * Checks if the node is a valid TypeScript numeric literal type.
 * @param node the node to be validated.
 * @returns true if the node is a TypeScript numeric literal type.
 * @private
 */

Calls:

  • isParentTSLiteralType
  • isGrandparentTSTypeAliasDeclaration
  • isGrandparentTSUnionType

Internal Comments:

// For negative numbers, use the parent node
// If the parent node is not a TSLiteralType, early return
// If the grandparent is a TSTypeAliasDeclaration, ignore
// If the grandparent is a TSUnionType and it's parent is a TSTypeAliasDeclaration, ignore

Code
function isTSNumericLiteralType(node: TSESTree.Node): boolean {
  // For negative numbers, use the parent node
  if (
    node.parent?.type === AST_NODE_TYPES.UnaryExpression &&
    node.parent.operator === '-'
  ) {
    node = node.parent;
  }

  // If the parent node is not a TSLiteralType, early return
  if (!isParentTSLiteralType(node)) {
    return false;
  }

  // If the grandparent is a TSTypeAliasDeclaration, ignore
  if (isGrandparentTSTypeAliasDeclaration(node)) {
    return true;
  }

  // If the grandparent is a TSUnionType and it's parent is a TSTypeAliasDeclaration, ignore
  if (isGrandparentTSUnionType(node)) {
    return true;
  }

  return false;
}

isParentTSReadonlyPropertyDefinition(node: TSESTree.Literal): boolean

Checks if the node parent is a readonly class property

Parameters:

  • node any: the node to be validated.

Returns: undefined true if the node parent is a readonly class property

Tags: @private

Raw JSDoc
/**
 * Checks if the node parent is a readonly class property
 * @param node the node to be validated.
 * @returns true if the node parent is a readonly class property
 * @private
 */

Calls:

  • getLiteralParent
Code
function isParentTSReadonlyPropertyDefinition(node: TSESTree.Literal): boolean {
  const parent = getLiteralParent(node);

  if (parent?.type === AST_NODE_TYPES.PropertyDefinition && parent.readonly) {
    return true;
  }

  return false;
}

isAncestorTSIndexedAccessType(node: TSESTree.Literal): boolean

Checks if the node is part of a type indexed access (eg. Foo[4])

Parameters:

  • node any: the node to be validated.

Returns: undefined true if the node is part of an indexed access

Tags: @private

Raw JSDoc
/**
 * Checks if the node is part of a type indexed access (eg. Foo[4])
 * @param node the node to be validated.
 * @returns true if the node is part of an indexed access
 * @private
 */

Calls:

  • getLiteralParent

Internal Comments:

// Handle unary expressions (eg. -4) (x2)
// Go up another level while we’re part of a type union (eg. 1 | 2) or
// intersection (eg. 1 & 2)

Code
function isAncestorTSIndexedAccessType(node: TSESTree.Literal): boolean {
  // Handle unary expressions (eg. -4)
  let ancestor = getLiteralParent(node);

  // Go up another level while we’re part of a type union (eg. 1 | 2) or
  // intersection (eg. 1 & 2)
  while (
    ancestor?.parent?.type === AST_NODE_TYPES.TSUnionType ||
    ancestor?.parent?.type === AST_NODE_TYPES.TSIntersectionType
  ) {
    ancestor = ancestor.parent;
  }

  return ancestor?.parent?.type === AST_NODE_TYPES.TSIndexedAccessType;
}

Type Aliases

Options

type Options = InferOptionsTypeFromRule<typeof baseRule>;

MessageIds

type MessageIds = InferMessageIdsTypeFromRule<typeof baseRule>;

Generated by Syntax Scribe