TypeScript continues to evolve at a rapid pace. The 5.x versions brought changes that go beyond performance improvements: they redefine patterns we have been using for years.
Table of contents
Open Table of contents
Standard decorators (TC39 Stage 3)
Finally. After years with the experimental version, TypeScript 5.0 adopted the standard decorators from TC39. The syntax is similar but the semantics changed quite a bit.
// Class decorator — before (experimental)
@sealed
class OldClass { ... }
// Standard decorator — TS 5.x
function logged<T extends new (...args: unknown[]) => unknown>(
target: T,
_ctx: ClassDecoratorContext,
) {
return class extends target {
constructor(...args: unknown[]) {
super(...args);
console.log(`[LOG] Instance of ${target.name} created`);
}
};
}
@logged
class UserService {
constructor(private db: Database) {}
}decorators.ts
Method and accessor decorators
function measure(_target: unknown, ctx: ClassMethodDecoratorContext) {
const name = String(ctx.name);
return function (this: unknown, ...args: unknown[]) {
const start = performance.now();
const result = (this as Record<string, Function>)[name](...args);
const result = Reflect.apply(
_target as Function,
this,
args
);
console.log(`${name} took ${performance.now() - start}ms`);
return result;
};
}
class ReportService {
@measure
async generatePDF(id: string) {
/* ... */
}
}method-decorator.ts
const Type Parameters
Before you needed as const on every call to infer literal tuples. Now you can declare it in the generic:
// Before: inferred as string[]
function head<T>(arr: T[]) {
return arr[0];
}
head(["a", "b"]); // type: string
// Now: inferred as the exact literal
function head<const T extends readonly unknown[]>(arr: T) {
return arr[0];
}
head(["a", "b"] as const); // type: "a"
head(["a", "b"]); // type: "a" ← works without as constconst-type-params.ts
satisfies operator (consolidated)
Introduced in 4.9 but already part of the daily workflow. It allows validating that a value satisfies a type without “widening” it:
type Palette = {
red: [number, number, number] | string;
green: [number, number, number] | string;
blue: [number, number, number] | string;
};
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
} satisfies Palette;
// Now TypeScript knows that red is a tuple, not string
palette.red.at(0); // ✓ — before it threw an errorsatisfies.ts
Improvements in infer inference
// Extract the return type filtered by constraint
type ReturnIfString<T> = T extends () => infer R extends string
? R
: never;
type A = ReturnIfString<() => "hello">; // "hello"
type B = ReturnIfString<() => number>; // neverinfer-extends.ts
Performance: --incremental and --composite mode
TS 5.x optimized incremental builds. In large projects the improvement can be up to 3×:
{
"compilerOptions": {
"composite": true,
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo",
"moduleResolution": "bundler"
}
}tsconfig.json
Tip: combine
compositewith project references (references) for monorepos. Each package will compile only what changed.
Quick summary
| Feature | Version | Impact |
|---|---|---|
| Standard decorators | 5.0 | High — replaces experimental |
const type params | 5.0 | Medium — less as const |
satisfies | 4.9 / consolidated in 5.x | High — more expressive typing |
infer ... extends | 5.x | Medium — more precise conditional types |
| Improved incremental build | 5.x | High in monorepos |