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:
[lint.rules]
"@typescript-eslint/no-magic-numbers" = "error" # or "warn"
Every option, set to its default:
[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
Reported:
function total(price: number) {
return price + price * 0.2;
}
Not reported:
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:
enum Status {
Active = 1,
}
type Level = 2 | 3;
type First = [string, number][0];
class Client {
readonly retries = 4;
}
Options
| Option | Default | What it does |
|---|---|---|
ignore | [] | Numbers that are never reported. 1 doesn't cover -1. Write a bigint as a string: "1n". |
ignoreArrayIndexes | false | Allow a number as an index: items[0]. |
ignoreDefaultValues | false | Allow a number as a default value: function retry(times = 3) {}. |
ignoreClassFieldInitialValues | false | Allow a number as the initial value of a class field that isn't readonly. |
ignoreReadonlyClassProperties | false | Allow numbers in readonly class fields. |
ignoreEnums | false | Allow numbers in enum members. |
ignoreNumericLiteralTypes | false | Allow a type alias for a number or a union of numbers. |
ignoreTypeIndexes | false | Allow a number as an index into a type: Pair[0]. |
enforceConst | false | Report a number that initializes a let or a var. |
detectObjects | false | Also report numbers in object literals and numbers assigned to a property. |
Not reported with ignore: [0, 1]:
function last(items: string[]) {
return items.length === 0 ? undefined : items[items.length - 1];
}
Not reported with ignoreArrayIndexes and ignoreDefaultValues:
function pick(items: string[], count = 3) {
return [items[0], items.slice(count)];
}
Not reported with ignoreEnums, ignoreNumericLiteralTypes, ignoreTypeIndexes and ignoreReadonlyClassProperties:
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:
class Client {
retries = 4;
readonly timeout = 5000;
}
Reported with enforceConst:
let timeout = 5000;
Both lines are reported with detectObjects:
const config = { timeout: 5000 };
config.timeout = 3000;