JavaScript got const in 2015 with ES6, and the rule it enforces is narrow: the binding cannot be reassigned. The value can still mutate. A const object is freely mutable. A const array can be pushed to. For real immutability you reach for Object.freeze() (ES5, 2009) or — the modern winner — TypeScript's as const assertion (TS 3.4, 2019), which gives compile-time readonly types with zero runtime cost. This guide covers all three plus the built-in numeric and Math constants every JS engineer should know cold.
What does const actually guarantee?
const is a block-scoped declaration that prevents reassignment of the identifier. It must be initialized at the declaration site — there is no separate "declare then assign" pattern. After that, the binding is permanent within its block; the value at the other end of the binding is not.
const MAX_RETRIES = 3;
const API_BASE_URL = 'https://api.example.com/v1';
const TIMEOUT_MS = 30_000;
// MAX_RETRIES = 5;
// → TypeError: Assignment to constant variable
const config = { timeout: 5000 };
config.timeout = 10_000; // OK — mutating a property
config.added = true; // OK — adding a property
// config = {}; // TypeError — rebindingThe rule of thumb: use const by default. Reach for let only when you genuinely need reassignment. Never use var in modern code — its function-scoping and hoisting behaviour are what let and const were introduced to replace.
How do you make an object truly immutable?
Object.freeze() makes an object's own properties read-only. In strict mode (the default in ES modules), an attempted write throws TypeError. In sloppy mode, the write is silently dropped — which is the worse of the two failure modes because the bug only surfaces under conditions you do not control.
const CONFIG = Object.freeze({
maxUploadSize: 10 * 1024 * 1024, // 10 MiB
allowedFormats: Object.freeze(['png', 'jpg', 'webp']),
api: Object.freeze({
baseUrl: 'https://api.example.com',
timeout: 5000,
}),
});
// CONFIG.maxUploadSize = 0; // TypeError in strict mode
// CONFIG.allowedFormats.push('gif'); // TypeError — inner array is frozen tooObject.freeze() is shallow. Nested objects must be frozen individually, or you write a recursive deep-freeze utility. In production codebases the runtime cost of freeze is usually irrelevant, but the mental cost of remembering "freeze every level" is enough that most teams now prefer TypeScript's as const for compile-time immutability.
Why is "as const" the modern winner?
TypeScript's as const assertion (added in TS 3.4) tells the compiler to infer the narrowest possible type for a literal. Object properties become readonly with literal types. Arrays become readonly tuples. There is zero runtime cost — it is purely a compile-time check that the rest of your code will not mutate the value.
const HTTP_STATUS = {
Ok: 200,
Created: 201,
BadRequest: 400,
NotFound: 404,
ServerError: 500,
} as const;
// type: { readonly Ok: 200; readonly Created: 201; ... }
type HttpStatus = typeof HTTP_STATUS[keyof typeof HTTP_STATUS];
// 200 | 201 | 400 | 404 | 500
const DIRECTIONS = ['north', 'south', 'east', 'west'] as const;
type Direction = typeof DIRECTIONS[number];
// 'north' | 'south' | 'east' | 'west'Pair as const with satisfies (TS 4.9, 2022) when you want both the literal narrowing and a structural check against an interface:
type Theme = { primary: string; spacing: number };
const THEME = {
primary: '#6366f1',
spacing: 8,
} as const satisfies Theme;
// THEME.primary still has the literal type "#6366f1"
// — but TypeScript verifies the shape matches Theme"as const" or enum?
TypeScript enum emits runtime code (a reverse-mapped object), which defeats tree-shaking and adds bytes to your bundle. The official TS team has nudged toward as const for years, and TS 5.0 (2023) added const enum inlining as a partial mitigation, but as const remains the cleaner default for new code.
// enum — generates runtime code
enum Direction { Up = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT' }
// as const — purely compile-time, tree-shakeable
const DIRECTION = {
Up: 'UP',
Down: 'DOWN',
Left: 'LEFT',
Right: 'RIGHT',
} as const;
type Direction = typeof DIRECTION[keyof typeof DIRECTION];Which built-in constants should you know?
JavaScript exposes a handful of numeric limits and mathematical constants on Number and Math. The two most footgun-prone are Number.MAX_SAFE_INTEGER (2^53 − 1 = 9_007_199_254_740_991) — beyond it, integer arithmetic loses precision — and Number.EPSILON, the smallest gap between two representable doubles, used for safe float comparison.
// Numeric limits
Number.MAX_SAFE_INTEGER // 9007199254740991 (use BigInt above this)
Number.MIN_SAFE_INTEGER // -9007199254740991
Number.MAX_VALUE // ~1.8e308
Number.MIN_VALUE // ~5e-324 (smallest positive — not the most negative)
Number.EPSILON // ~2.2e-16
Number.POSITIVE_INFINITY // Infinity
Number.NEGATIVE_INFINITY // -Infinity
Number.NaN // NaN (use Number.isNaN, not ===)
// Math constants
Math.PI // 3.141592653589793
Math.E // 2.718281828459045
Math.LN2 // 0.6931471805599453
Math.LN10 // 2.302585092994046
Math.SQRT2 // 1.4142135623730951Number.MIN_VALUE is the most-misnamed constant in the language: it is the smallest positive double, not the most negative number. For the most-negative finite double, use -Number.MAX_VALUE.
Practical patterns and gotchas
- Numeric separators:
1_000_000beats1000000,0xFF_FFbeats0xFFFF. Shipping in V8 since 2019, in every modern engine. - Use === for NaN comparison? No.
NaN === NaNisfalse. UseNumber.isNaN(x). - Don't mutate frozen arrays at the type level: TypeScript's
readonlytuples fromas constreject.push()at compile time, which is usually what you want. - Module-level const is your config file: define shared constants in a dedicated module and import them. Don't repeat literal values across files — see constants best practices for naming and organization rules.
- Avoid TypeScript enum in new code unless your team already uses it heavily.
as const+ a derived union type is the modern idiom and tree-shakes cleanly.
For language-agnostic naming, immutability, and "magic number" rules, see constants best practices and constants tips & tricks.