TypeScript 5.x New Features: Complete Guide to Modern TypeScript
in Development on Typescript, JavaScript
TypeScript 5.x New Features: Complete Guide to Modern TypeScript
TypeScript 5.x has brought significant improvements to the language, making it more powerful and developer-friendly than ever. This comprehensive guide covers all the major features you need to know in 2026.
Decorators (Stage 3)
TypeScript 5.0 introduced support for the ECMAScript Stage 3 decorators proposal:
Class Decorators
function logged<T extends new (...args: any[]) => any>(
target: T,
context: ClassDecoratorContext
) {
return class extends target {
constructor(...args: any[]) {
console.log(`Creating instance of ${context.name}`);
super(...args);
}
};
}
@logged
class UserService {
constructor(private userId: string) {}
}
const service = new UserService("123");
// Logs: Creating instance of UserService
Method Decorators
function measure<T extends (...args: any[]) => any>(
target: T,
context: ClassMethodDecoratorContext
) {
return function (this: any, ...args: Parameters<T>): ReturnType<T> {
const start = performance.now();
const result = target.apply(this, args);
const end = performance.now();
console.log(`${String(context.name)} took ${end - start}ms`);
return result;
};
}
class DataProcessor {
@measure
processData(data: number[]) {
return data.map(x => x * 2);
}
}
Field Decorators
function validate(min: number, max: number) {
return function <T>(
target: undefined,
context: ClassFieldDecoratorContext
) {
return function (initialValue: T): T {
if (typeof initialValue === 'number') {
if (initialValue < min || initialValue > max) {
throw new Error(
`${String(context.name)} must be between ${min} and ${max}`
);
}
}
return initialValue;
};
};
}
class Product {
@validate(0, 100)
discount = 10;
}
const Type Parameters
Infer more specific literal types with const type parameters:
// Without const - infers string[]
function getRoutes<T extends readonly string[]>(routes: T) {
return routes;
}
const routes1 = getRoutes(["home", "about"]); // string[]
// With const - infers readonly ["home", "about"]
function getRoutesConst<const T extends readonly string[]>(routes: T) {
return routes;
}
const routes2 = getRoutesConst(["home", "about"]);
// readonly ["home", "about"]
// Practical example
function createConfig<const T extends Record<string, unknown>>(config: T): T {
return config;
}
const config = createConfig({
apiUrl: "https://api.example.com",
timeout: 5000,
features: ["auth", "analytics"] as const
});
// Type: { apiUrl: "https://api.example.com"; timeout: 5000; features: readonly ["auth", "analytics"] }
satisfies Operator
Validate types without changing the inferred type:
type Colors = "red" | "green" | "blue";
type RGB = [number, number, number];
// Without satisfies - loses specific type info
const palette1: Record<Colors, string | RGB> = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255]
};
// palette1.green is string | RGB
// With satisfies - keeps specific types
const palette2 = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255]
} satisfies Record<Colors, string | RGB>;
// palette2.green is string
// palette2.red is [number, number, number]
// Catches errors at definition time
const palette3 = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
// @ts-error - 'yellow' is not assignable
yellow: "#ffff00"
} satisfies Record<Colors, string | RGB>;
Improved Enums
Union Enums
enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE",
Pending = "PENDING"
}
// TypeScript 5.x treats this as a union
type StatusType = `${Status}`; // "ACTIVE" | "INACTIVE" | "PENDING"
function handleStatus(status: Status) {
// Exhaustive checking works naturally
switch (status) {
case Status.Active:
return "User is active";
case Status.Inactive:
return "User is inactive";
case Status.Pending:
return "User is pending";
}
}
Type-Only Imports and Exports
Explicit Type Imports
// Explicit type-only import
import type { User, Config } from "./types";
import { createUser } from "./utils";
// Mixed import with inline type
import { createUser, type User } from "./module";
// Type-only export
export type { User, Config };
export { createUser };
Template Literal Types Improvements
Intrinsic String Manipulation
type Greeting = "Hello, World!";
type Upper = Uppercase<Greeting>; // "HELLO, WORLD!"
type Lower = Lowercase<Greeting>; // "hello, world!"
type Capital = Capitalize<Greeting>; // "Hello, World!"
type Uncap = Uncapitalize<Greeting>; // "hello, World!"
// Practical example: API route types
type HttpMethod = "get" | "post" | "put" | "delete";
type Route = "/users" | "/posts" | "/comments";
type ApiEndpoint = `${Uppercase<HttpMethod>} ${Route}`;
// "GET /users" | "GET /posts" | "GET /comments" | "POST /users" | ...
Pattern Matching with Template Literals
type ExtractRouteParams<T extends string> =
T extends `${infer _Start}:${infer Param}/${infer Rest}`
? Param | ExtractRouteParams<`/${Rest}`>
: T extends `${infer _Start}:${infer Param}`
? Param
: never;
type Params = ExtractRouteParams<"/users/:userId/posts/:postId">;
// "userId" | "postId"
Improved Type Inference
Better Control Flow Analysis
function processValue(value: string | number | null) {
if (value === null) {
return;
}
// TypeScript now correctly narrows in more complex scenarios
const result = typeof value === "string"
? value.toUpperCase()
: value.toFixed(2);
return result;
}
Improved Generic Inference
// Better inference for generic functions
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const merged = merge(
{ name: "John", age: 30 },
{ email: "john@example.com" }
);
// Correctly inferred as { name: string; age: number; email: string }
Performance Improvements
Faster Compilation
TypeScript 5.x includes significant performance improvements:
// tsconfig.json - Enable incremental builds
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo"
}
}
Project References
// tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true
},
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/utils" }
]
}
New Configuration Options
verbatimModuleSyntax
{
"compilerOptions": {
"verbatimModuleSyntax": true
}
}
// With verbatimModuleSyntax, this import is preserved
import { something } from "./module";
// Type-only imports are always removed
import type { SomeType } from "./types";
allowImportingTsExtensions
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"noEmit": true
}
}
// Now allowed
import { helper } from "./utils.ts";
Practical Examples
Type-Safe Event Emitter
type EventMap = {
userCreated: { userId: string; email: string };
userDeleted: { userId: string };
orderPlaced: { orderId: string; amount: number };
};
class TypedEventEmitter<T extends Record<string, unknown>> {
private listeners = new Map<keyof T, Set<(data: any) => void>>();
on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event)!.add(listener);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
this.listeners.get(event)?.forEach(listener => listener(data));
}
}
const emitter = new TypedEventEmitter<EventMap>();
emitter.on("userCreated", (data) => {
// data is { userId: string; email: string }
console.log(data.email);
});
emitter.emit("userCreated", { userId: "123", email: "test@example.com" });
Builder Pattern with Types
class QueryBuilder<T extends object = {}> {
private query: T = {} as T;
select<K extends string>(field: K): QueryBuilder<T & Record<K, true>> {
return this as any;
}
where<K extends string, V>(
field: K,
value: V
): QueryBuilder<T & Record<`where_${K}`, V>> {
return this as any;
}
build(): T {
return this.query;
}
}
const query = new QueryBuilder()
.select("name")
.select("email")
.where("age", 25)
.build();
// Type: { name: true; email: true; where_age: number }
Migrating to TypeScript 5.x
Step-by-Step Migration
- Update TypeScript
npm install typescript@latest - Update tsconfig.json
{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true, "verbatimModuleSyntax": true } } - Update decorator syntax (if using experimentalDecorators) ```typescript // Old (experimental) function Log(target: any, key: string) { }
// New (Stage 3) function Log
Conclusion
TypeScript 5.x brings powerful new features that improve type safety, developer experience, and performance. Key takeaways:
- Use Stage 3 decorators for cleaner metadata patterns
- Leverage
consttype parameters for literal inference - Apply
satisfiesfor validation without type widening - Enable
verbatimModuleSyntaxfor clearer imports - Take advantage of performance improvements with incremental builds
Resources
이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)
