number

Types every JavaScript number — integers and floats alike, all IEEE 754 doubles.

Signature
let myAge: number = 42;
  • Added in TypeScript 1.0

Errors

Assigning a Number wrapper object to a number is a compile error: "Type 'Number' is not assignable to type 'number'."

Changelog

  • PHP 2.7Numeric separators (1_000_000) are permitted in numeric literals.
  • PHP 3.2bigint added as a separate primitive for integers beyond the safe range; it does not mix with number.
  • PHP 4.1Template literal types allow `${number}` patterns, e.g. `${number}px`.

number is the single numeric type for ordinary JavaScript values. As the handbook puts it, JavaScript has no special runtime value for integers, so there is no int or float — 42, 3.14, 0xff, 1e3 and -0 are all number.

What the type does not tell you

Every number is an IEEE 754 double, and the type carries none of the consequences:

  • 0.1 + 0.2 is 0.30000000000000004. Type-checking will not warn you; use integer minor units for money, or compare with a tolerance.
  • Integers are exact only up to Number.MAX_SAFE_INTEGER (253−1). Beyond it, distinct values collapse onto each other silently. That is what bigint is for.
  • NaN and ±Infinity are members of number, so a function typed (n: number) => number can return NaN with no complaint. Validate with Number.isFinite.

Narrowing it

Numeric literal unions (0 | 1 | 2) constrain the value to a known set, which is useful for exit codes, HTTP statuses and enum-like flags. As with string, the capitalised Number is the boxed wrapper and should never be used as an annotation.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Annotating counts, offsets and measurements where 2^53 is comfortably out of reach
  • Constraining a status or exit code with a numeric literal union such as 0 | 1 | 2
  • Deciding a monetary amount should be stored as integer cents rather than a float

Frequently asked questions

Does TypeScript have separate int and float types?

No. JavaScript has one numeric representation — the IEEE 754 double — so TypeScript has one `number` type covering integers, floats, hex, and exponential literals. Use `Number.isInteger` for a runtime check, a numeric literal union to restrict the allowed values, and `bigint` when integers can exceed `Number.MAX_SAFE_INTEGER`.

When should I use bigint instead of number?

When integers can exceed 2^53−1 (`Number.MAX_SAFE_INTEGER`) and every digit matters — 64-bit database or snowflake IDs, token amounts, cryptographic values. `number` silently rounds beyond that range. `bigint` requires `target: ES2020` or higher and never mixes with `number` in arithmetic without an explicit conversion.

Why does 0.1 + 0.2 not equal 0.3 in TypeScript?

Because `number` is an IEEE 754 binary double and 0.1 and 0.2 have no exact binary representation, so the sum is `0.30000000000000004`. TypeScript does not change runtime arithmetic, so type-checking cannot catch this. Store money as integer minor units, or compare with a tolerance such as `Math.abs(a - b) < Number.EPSILON`.

More examples

Money in integer minor units

Prints `2748 $27.48`, then `27.479999999999997` and `false`. Both calculations are typed `number` and both compile, but only the integer one is exact. The alias `Cents` is documentation, not enforcement — a plain `number` still assigns to it; use a branded type if you need the compiler to keep the units apart.

Example · typescript
type Cents = number; // an alias documents intent; it does not add checking

const prices: Cents[] = [1999, 499, 250];
const totalCents = prices.reduce((a, b) => a + b, 0);

function format(cents: Cents): string {
  const sign = cents < 0 ? "-" : "";
  const abs = Math.abs(cents);
  return `${sign}$${Math.floor(abs / 100)}.${String(abs % 100).padStart(2, "0")}`;
}

console.log(totalCents, format(totalCents));

// The same sum in floats:
const floats = [19.99, 4.99, 2.5];
console.log(floats.reduce((a, b) => a + b, 0));
console.log(floats.reduce((a, b) => a + b, 0) === 27.48);

Numeric literal unions and validation

Prints `[ 'success', 'general error', 'misuse', 'interrupted' ]` then `8080 null null null`. The literal union makes the `switch` exhaustive without a `default`, so tsc can prove a `string` is always returned. `parsePort` shows why the `number` type alone is not validation: `Number("abc")` is `NaN` and `Number("")` is `0`, both perfectly valid `number` values.

Example · typescript
type ExitCode = 0 | 1 | 2 | 130;

function describe(code: ExitCode): string {
  switch (code) {
    case 0: return "success";
    case 1: return "general error";
    case 2: return "misuse";
    case 130: return "interrupted";
  }
}

function parsePort(raw: string): number | null {
  const n = Number(raw);
  // Number("") is 0 and Number("abc") is NaN — both need catching
  if (!Number.isInteger(n) || n <= 0 || n > 65535) return null;
  return n;
}

console.log([0, 1, 2, 130].map((c) => describe(c as ExitCode)));
console.log(parsePort("8080"), parsePort("abc"), parsePort(""), parsePort("99999"));
// describe(3) // Error: Argument of type '3' is not assignable to 'ExitCode'.

Discussion

  • Be the first to comment on this lesson.