TC39 Decorators in 2026: The Definitive Guide
A complete guide to the TC39 Stage 3 decorators proposal with practical examples for classes, methods, and fields.
Decorators Have Arrived
After years of proposals and revisions, TC39 decorators have reached Stage 3 and are shipping in modern JavaScript engines. Unlike the legacy TypeScript experimental decorators, the new standard decorators have a clean, well-defined API. If you've used decorators in TypeScript or Babel, the new syntax will feel familiar but the semantics are different.
Decorator Basics
A decorator is a function that receives the decorated value and a context object, and optionally returns a replacement value.
``javascript
// Method decorator
function logged(originalMethod, context) {
const methodName = context.name;
function replacementMethod(...args) {
console.log(\Calling \${methodName} with\, args);
const result = originalMethod.call(this, ...args);
console.log(\\${methodName} returned\, result);
return result;
}
return replacementMethod; }
class Calculator { @logged add(a, b) { return a + b; } }
const calc = new Calculator();
calc.add(2, 3);
// "Calling add with [2, 3]"
// "add returned 5"
`
Field Decorators
Field decorators use the \context.access\ object and an initializer to transform field values.
`javascript
function clamp(min, max) {
return function (value, context) {
return function (initialValue) {
return Math.min(max, Math.max(min, initialValue));
};
};
}
function readonly(value, context) { context.addInitializer(function () { Object.defineProperty(this, context.name, { writable: false, value: this[context.name] }); }); }
class Config { @clamp(0, 100) volume = 150; // Will be clamped to 100
@readonly version = '1.0.0'; }
const config = new Config();
console.log(config.volume); // 100
config.version = '2.0.0'; // TypeError in strict mode
`
Class Decorators
Class decorators can wrap or replace the entire class.
`javascript
function singleton(Class, context) {
let instance;
return class extends Class { constructor(...args) { if (instance) return instance; super(...args); instance = this; } }; }
@singleton class Database { constructor(url) { this.url = url; console.log('Creating database connection'); } }
const db1 = new Database('postgres://localhost');
const db2 = new Database('postgres://remote');
console.log(db1 === db2); // true
`
Composing Decorators
Decorators compose naturally — they apply bottom-up (closest to the target first).
`javascript
class UserService {
@logged
@cached(60_000)
@rateLimit(100)
async getUser(id) {
return await fetch(\/api/users/\${id}\).then(r => r.json());
}
}
``
The execution order is: rateLimit wraps first, then cached wraps that, then logged wraps the outermost layer. TC39 decorators are a game-changer for writing clean, reusable cross-cutting concerns in JavaScript.