Back to blog

TypeScript 5.9 and ECMAScript 2025 Approved: What Changes For JavaScript Developers

Hello HaWkers, 2025 was a transformative year for the JavaScript ecosystem. ECMAScript 2025 was officially approved by TC39, bringing features the community had been asking for years, and TypeScript 5.8 and 5.9 arrived with full support for these new additions. If you work with JavaScript or TypeScript, you need to understand what changed.

The Stack Overflow 2025 survey showed that 78% of JavaScript developers already use TypeScript in production. This is no longer a trend, it's the industry standard.

ECMAScript 2025: The Official Features

ES2025 brought additions that will simplify code that previously required external libraries or complex workarounds. Let's look at each in detail.

Promise.withResolvers

We finally have a clean way to create Promises with external resolvers:

// BEFORE: Verbose pattern
let resolve: (value: string) => void;
let reject: (reason: any) => void;

const promise = new Promise<string>((res, rej) => {
  resolve = res;
  reject = rej;
});

// Use resolve and reject elsewhere
setTimeout(() => resolve('done'), 1000);

// NOW: ES2025 with Promise.withResolvers
const { promise, resolve, reject } = Promise.withResolvers<string>();

// Much cleaner
setTimeout(() => resolve('done'), 1000);

This is especially useful when you need to resolve a Promise from outside its callback, like in event systems or queues.

Grouping with Object.groupBy and Map.groupBy

Grouping arrays by a property was one of the most common operations that required lodash:

interface Product {
  name: string;
  category: string;
  price: number;
}

const products: Product[] = [
  { name: 'iPhone', category: 'electronics', price: 999 },
  { name: 'MacBook', category: 'electronics', price: 1999 },
  { name: 'Shirt', category: 'clothing', price: 49 },
  { name: 'Jeans', category: 'clothing', price: 89 },
];

// Object.groupBy returns an object
const byCategory = Object.groupBy(products, (p) => p.category);
// {
//   electronics: [{ name: 'iPhone', ... }, { name: 'MacBook', ... }],
//   clothing: [{ name: 'Shirt', ... }, { name: 'Jeans', ... }]
// }

// Map.groupBy returns a Map (useful for non-string keys)
const byPriceRange = Map.groupBy(products, (p) =>
  p.price > 500 ? 'expensive' : 'affordable'
);

Native Decorators (Stage 3 Finalized)

Decorators finally arrived in JavaScript natively, with syntax aligned with what experimental TypeScript offered:

// Logging decorator
function log(target: any, context: ClassMethodDecoratorContext) {
  const methodName = String(context.name);

  return function (...args: any[]) {
    console.log(`Calling ${methodName} with`, args);
    const result = target.apply(this, args);
    console.log(`${methodName} returned`, result);
    return result;
  };
}

// Memoization decorator
function memoize(target: any, context: ClassMethodDecoratorContext) {
  const cache = new Map();

  return function (...args: any[]) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = target.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

class Calculator {
  @log
  @memoize
  fibonacci(n: number): number {
    if (n <= 1) return n;
    return this.fibonacci(n - 1) + this.fibonacci(n - 2);
  }
}

const calc = new Calculator();
calc.fibonacci(40); // Calculates and caches
calc.fibonacci(40); // Returns from cache instantly

Set Methods

Set operations that previously needed manual code:

const frontend = new Set(['React', 'Vue', 'Angular', 'Svelte']);
const popular = new Set(['React', 'Vue', 'Node.js', 'Python']);

// Intersection: frameworks that are frontend AND popular
const frontendPopular = frontend.intersection(popular);
// Set { 'React', 'Vue' }

// Union: all frameworks
const all = frontend.union(popular);
// Set { 'React', 'Vue', 'Angular', 'Svelte', 'Node.js', 'Python' }

// Difference: frontend that are not popular (in this list)
const nicheFrameworks = frontend.difference(popular);
// Set { 'Angular', 'Svelte' }

// Symmetric difference: exclusive to each set
const exclusive = frontend.symmetricDifference(popular);
// Set { 'Angular', 'Svelte', 'Node.js', 'Python' }

// Checks
frontend.isSubsetOf(all);      // true
all.isSupersetOf(frontend);    // true
frontend.isDisjointFrom(new Set(['Java', 'C++'])); // true

TypeScript 5.8 and 5.9: Significant Improvements

Beyond ES2025 support, the latest TypeScript versions brought important improvements of their own.

Enhanced Type Inference

// TS 5.9 infers better types in complex callbacks
const users = [
  { name: 'Alice', age: 30, role: 'admin' as const },
  { name: 'Bob', age: 25, role: 'user' as const },
];

// Before: needed explicit annotation
// Now: TypeScript infers correctly
const admins = users.filter(u => u.role === 'admin');
// admins is inferred as { name: string; age: number; role: 'admin' }[]

Satisfies with Improved Generics

interface Config<T> {
  value: T;
  validate: (v: T) => boolean;
}

// satisfies now works better with generics
const numberConfig = {
  value: 42,
  validate: (v) => v > 0, // v is inferred as number
} satisfies Config<number>;

const stringConfig = {
  value: 'hello',
  validate: (v) => v.length > 0, // v is inferred as string
} satisfies Config<string>;

Type Checking Performance Improvements

TypeScript 5.9 brought significant optimizations:

Compilation benchmarks:

  • Large projects: 15-20% faster
  • Incremental builds: 25-30% faster
  • Editor responsiveness: 40% better in complex files

TypeScript Adoption in 2025

The numbers show that TypeScript has consolidated as the standard:

Stack Overflow 2025 Survey:

  • 78% of JavaScript developers use TypeScript in production
  • 92% of new enterprise applications start with TypeScript
  • 85% of most popular npm packages have types (native or @types)

Why adoption grew so much:

  • Frameworks like Next.js, Nuxt, SvelteKit have TypeScript as default
  • AI tools like GitHub Copilot work better with types
  • Code maintenance time significantly reduced
  • Safe refactoring in large projects

How to Update

If you haven't migrated to the latest versions yet, here's how:

# Update TypeScript
npm install typescript@5.9 --save-dev

# Update tsconfig.json
{
  "compilerOptions": {
    "target": "ES2025",
    "lib": ["ES2025", "DOM"],
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

Migration Checklist

To use ES2025 features:

  • Update your bundler (Vite, webpack, esbuild)
  • Check browser compatibility if doing frontend
  • Use polyfills if you need to support old browsers
  • Update @types/node if using Node.js

The Future: ES2026 and TypeScript 6.0

There are already Stage 3 proposals for ES2026:

Proposals in progress:

  • Pattern Matching (match expression)
  • Pipeline Operator (|>)
  • Records and Tuples (immutable types)
  • Temporal API (replace Date)

TypeScript 6.0 is planned for 2026 with possible breaking changes to clean up legacy features.

Conclusion

ECMAScript 2025 and TypeScript 5.9 represent the maturity of the JavaScript ecosystem. Features like Promise.withResolvers, groupBy, Set methods, and native decorators eliminate the need for external dependencies for common operations.

If you still use plain JavaScript, 2025 is the year to migrate to TypeScript. The benefits of type safety, better tooling, and built-in documentation outweigh any initial learning curve.

If you want to continue learning about the JavaScript ecosystem, I recommend checking out the article on Functional Programming in JavaScript where we explore concepts that complement these new features.

Let's go! 🦅

Comments (0)

This article has no comments yet 😢. Be the first! 🚀🦅

Add comments