@typescript-eslint/no-magic-numbers

Disallow numbers that are used without a name

Also called typescript-eslint/no-magic-numbers, typescript/no-magic-numbers

A bare number in an expression doesn't say what it stands for. price * 0.2 could be a tax, a discount or a tip. If you write the same number in three places, you have to find all three to change it. A named const fixes both.

The rule doesn't report the number a variable is declared with, a value in an object literal, a number assigned to a property, the radix of parseInt() or a number in JSX. It reports every other number, including 0 and 1.

It's the TypeScript version of no-magic-numbers and takes the same options.

With bunfig.toml you can set either name. bun lint runs this rule in TypeScript files and no-magic-numbers in JavaScript files, so nothing is reported twice. With an ESLint config, turn on only one of the two.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"@typescript-eslint/no-magic-numbers" = "error" # or "warn"

Every option, set to its default:

bunfig.toml
[lint.rules]
"@typescript-eslint/no-magic-numbers" = ["error", {
  ignore = [], # list of numbers or strings
  ignoreArrayIndexes = false,
  ignoreDefaultValues = false,
  ignoreClassFieldInitialValues = false,
  ignoreReadonlyClassProperties = false,
  ignoreEnums = false,
  ignoreNumericLiteralTypes = false,
  ignoreTypeIndexes = false,
  enforceConst = false,
  detectObjects = false,
}]

Examples#

example.ts1 problem
function total(price: number) {
  return price + price * 0.2;
}
example.tsNo problems
const TAX_RATE = 0.2;

function total(price: number) {
  return price + price * TAX_RATE;
}

Numbers in enums, types and class fields count too. All five are reported:

example.ts5 problems
enum Status {
  Active = 1,
}

type Level = 2 | 3;

type First = [string, number][0];

class Client {
  readonly retries = 4;
}

Options#

OptionDefaultWhat it does
ignore[]Numbers that are never reported. 1 doesn't cover -1. Write a bigint as a string: "1n".
ignoreArrayIndexesfalseAllow a number as an index: items[0].
ignoreDefaultValuesfalseAllow a number as a default value: function retry(times = 3) {}.
ignoreClassFieldInitialValuesfalseAllow a number as the initial value of a class field that isn't readonly.
ignoreReadonlyClassPropertiesfalseAllow numbers in readonly class fields.
ignoreEnumsfalseAllow numbers in enum members.
ignoreNumericLiteralTypesfalseAllow a type alias for a number or a union of numbers.
ignoreTypeIndexesfalseAllow a number as an index into a type: Pair[0].
enforceConstfalseReport a number that initializes a let or a var.
detectObjectsfalseAlso report numbers in object literals and numbers assigned to a property.

Not reported with ignore: [0, 1]:

example.tswith { "ignore": [0, 1] }No problems
function last(items: string[]) {
  return items.length === 0 ? undefined : items[items.length - 1];
}

Not reported with ignoreArrayIndexes and ignoreDefaultValues:

example.tswith { "ignoreArrayIndexes": true, "ignoreDefaultValues": true }No problems
function pick(items: string[], count = 3) {
  return [items[0], items.slice(count)];
}

Not reported with ignoreEnums, ignoreNumericLiteralTypes, ignoreTypeIndexes and ignoreReadonlyClassProperties:

example.tswith { "ignoreEnums": true, "ignoreNumericLiteralTypes": true, "ignoreTypeIndexes": true, "ignoreReadonlyClassProperties": true }No problems
enum Status {
  Active = 1,
}

type Level = 2 | 3;

type First = [string, number][0];

class Client {
  readonly retries = 4;
}

With ignoreClassFieldInitialValues alone, the readonly field is still reported:

example.tswith { "ignoreClassFieldInitialValues": true }1 problem
class Client {
  retries = 4;
  readonly timeout = 5000;
}

Reported with enforceConst:

example.tswith { "enforceConst": true }1 problem
let timeout = 5000;

Both lines are reported with detectObjects:

example.tswith { "detectObjects": true }2 problems
const config = { timeout: 5000 };
config.timeout = 3000;