Check whether a value is an integer (Number.isInteger, Number.isSafeInteger)

JavaScript gives you two precise predicates for integer checks. Number.isInteger(value) answers “is this value a Number primitive with no fractional part?” Number.isSafeInteger(value) goes further: the value must be an integer and within the IEEE-754 “safe” range where every integer is represented exactly. Neither function coerces non-numbers. If you pass a string, boolean, null, or an object, they return false. These definitions are standardized and consistent across modern engines.

Number.isInteger(3);           // true
Number.isInteger(3.0);         // true (no fractional part)
Number.isInteger(3.14);        // false
Number.isInteger("3");         // false (no coercion)
Number.isSafeInteger(9007199254740991);  // true
Number.isSafeInteger(9007199254740992);  // false

Integers in a floating-point world

All ordinary JavaScript numbers are IEEE-754 double-precision floats, not a separate integer type. That’s why “integer-ness” is a property of a Number value, not a different runtime type. The engine deems a value an integer when it is finite and equal to its floor (or, equivalently, when the fractional part is zero). These semantics come directly from the language definition.

typeof 37;               // "number"
Number.isInteger(37);    // true
Number.isInteger(37.000); // true
Number.isInteger(-0);     // true (yes, -0 is an integer)

What “safe” means and where the boundary sits

Because Numbers have 53 bits of precision in their significand, every integer from −(2^53 − 1) through +(2^53 − 1) is exactly representable—this is the safe integer range. Outside that closed interval, seemingly different integers can collapse to the same binary64 value. JavaScript exposes the boundaries as Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER. Number.isSafeInteger(x) checks both integer-ness and that x lies within these bounds.

Number.MAX_SAFE_INTEGER; // 9007199254740991  (2^53 - 1)
Number.MIN_SAFE_INTEGER; // -9007199254740991 (-(2^53 - 1))

Number.isInteger(9007199254740993);      // true  (mathematically an integer…)
Number.isSafeInteger(9007199254740993);  // false (…but not safe as a Number)

9007199254740993 === 9007199254740992;   // true (!) — precision lost

No coercion: objects, strings, and boxed numbers

Both methods require a Number primitive. They return false for non-number types, including boxed numbers (new Number(5)) and numeric strings. If you intend to validate user input, convert it first with Number(), parseInt(), or parseFloat(), then check the resulting value with Number.isInteger/Number.isSafeInteger. The non-coercing behavior is part of the spec and intentionally differs from older global predicates.

Number.isInteger(new Number(5)); // false (object)
Number.isInteger("5");           // false (string)

const n = Number("5");
Number.isInteger(n);             // true
Number.isSafeInteger(n);         // true

Relationship to Number.isFinite and Number.isNaN

For an integer check to succeed, the value must also be finite and not NaN. You don’t need to test those separately—Number.isInteger already implies them—but it’s useful to remember that Infinity, -Infinity, and NaN fail the integer test even though they are of type number. This follows from the same numeric abstract operations the spec uses for all Number predicates.

Number.isInteger(Infinity);  // false
Number.isInteger(NaN);       // false
Number.isSafeInteger(NaN);   // false

BigInt vs Number: choose the right integer kind

If you need exact integers beyond the safe range, use BigInt. The integer checkers here are for Number values only; they don’t accept BigInt and will return false for them. Use typeof x === "bigint" (or logic specific to your domain) when you expect 64-bit+ magnitudes. The boundary constants and “safe” notion apply solely to Number, not BigInt.

const huge = 9007199254740993n;     // BigInt
Number.isInteger(huge);             // false (different type)
typeof huge === "bigint";           // true

Practical validation patterns you’ll actually use

For form inputs, API payloads, or CLI arguments, read once, then validate in two clear steps: convert to a Number, then assert integer-ness and optionally safety. This avoids accidental reliance on coercion and keeps your intent obvious in code review.

function readSafeInt(value) {
  const n = Number(value);
  return Number.isSafeInteger(n) ? n : null;
}

readSafeInt("42");                  // 42
readSafeInt("42.0");                // 42
readSafeInt("42.1");                // null
readSafeInt("9007199254740993");    // null (unsafe)

If you’re normalizing data that may include decimals, clamp only after a precise test. Avoid Math.round as a proxy for integer-ness; it can silently turn non-integers into integers. Let the predicate decide first.

function ensureInteger(n) {
  if (!Number.isInteger(n)) throw new TypeError("Expected an integer");
  return n;
}

ensureInteger(3.000); // ok
ensureInteger(3.14);  // throws

Fast mental checks and boundary probes

When you’re unsure how a value behaves at the edges, probe it with arithmetic that would reveal precision loss. Values just outside the safe range will behave strangely under equality and +/−1 increments—another signal to switch to BigInt or redesign storage. The examples below illustrate this cliff.

const a = 9007199254740991;  // MAX_SAFE_INTEGER
const b = a + 1;             // 9007199254740992
const c = a + 2;             // 9007199254740992 (!) same as b

Number.isInteger(b);         // true
Number.isSafeInteger(b);     // false
b === c;                     // true — distinct integers collapsed

References

SourceWhat it covers
MDN: Number.isInteger()Definition, non-coercing behavior, examples, and spec links.
MDN: Number.isSafeInteger()“Safe” definition, behavior, and examples.
MDN: Number.MAX_SAFE_INTEGERBoundary constant and explanation of 2^53 − 1.
MDN: Number.MIN_SAFE_INTEGERNegative boundary constant −(2^53 − 1).
MDN: NumberNumbers are IEEE-754 doubles; BigInt exists but is a different type.
ECMAScript® Language SpecificationNormative algorithms for Number.isInteger and Number.isSafeInteger.