What’s New in TypeScript 5.0? Explore the Biggest Changes and Features

This article reviews the major updates in TypeScript 5.0, covering new decorator support, the "bundler" moduleResolution option, const type parameters, unified enums, break changes, compiler option enhancements, JSDoc extensions, import‑sorting behavior, and performance improvements, all illustrated with code examples.

Alipay Experience Technology
Alipay Experience Technology
Alipay Experience Technology
What’s New in TypeScript 5.0? Explore the Biggest Changes and Features
The author, a former Ant Group frontend engineer, rewrote the article based on the TypeScript 5.0 Beta release notes; TypeScript officially enters the 5.0 era. Note: Gray‑background text was translated by ChatGPT, green text was manually corrected by the author.

Significant Changes Since the Beta

One new change is that TypeScript now allows decorators to appear before or after export and export default, reflecting discussions within TC39.

Another change is the new module resolution option bundler, which can only be used when the --module option is set to esnext. This ensures that import statements are resolved correctly regardless of whether the bundler or loader uses TypeScript’s module options; the import statements in the input file will not be transformed into require calls. The release notes recommend most library authors use node16 or nodenext.

Although the Beta already includes this feature, documentation for case‑insensitive import sorting in editor scenarios was not written because the custom UX is still under discussion; by default, TypeScript should now cooperate better with other tools.

Since the RC release, the most notable change is that TypeScript 5.0 specifies a minimum Node.js version of 12.20 in package.json. A migration article for modules is also linked.

Speed benchmarks and package‑size differences have been updated since the Beta and RC releases, with clearer benchmark names and a separate chart for package‑size improvements.

BreakChanges And Deprecations

Runtime Requirements

Requires Node.js 10.x or higher.

lib.d.ts Changes

Routine changes; see the pull request for details. The author will add manual fixes for any common usage errors encountered in real projects.

Implicit Type Conversion in Relational Operators Forbidden

Before 5.0, TypeScript only checked implicit type conversion for +, -, *, /. Example:

function func(ns: number | string) {
  return ns * 4; // Error, possible implicit coercion
}

After 5.0, relational operators >, <, <=, >= also produce errors:

function func(ns: number | string) {
  return ns > 4; // Now also an error
}

The classic + operator can still be used for explicit conversion.

function func(ns: number | string) {
  return +ns > 4; // OK
}

Enum Type Checks

Since TypeScript supports enums, version 5.0 addresses long‑standing oddities. When assigning a literal outside the enum range, an error is reported.

enum SomeEvenDigit {
  Zero = 0,
  Two = 2,
  Four = 4
}
// Error
let m: SomeEvenDigit = 1;

Mixed enums composed of multiple enum types now correctly report type errors for non‑numeric members.

enum Letters { A = "a" }
enum Numbers { one = 1, two = Letters.A }
// Error in 5.0: Numbers.two is not assignable to number
const t: number = Numbers.two;

Decorators

ECMAScript decorators have reached Stage 3, and TypeScript 5.0 implements the standard. A decorator is a function that can modify a class, method, or property.

Decorator Usage

Example of a class‑method decorator:

function loggedMethod(originalMethod: any, context: ClassMethodDecoratorContext) {
  const methodName = String(context.name);
  function replacementMethod(this: any, ...args: any[]) {
    console.log(`LOG: Entering method '${methodName}'.`);
    const result = originalMethod.call(this, ...args);
    console.log(`LOG: Exiting method '${methodName}'.`);
    return result;
  }
  return replacementMethod;
}

class Person {
  name: string;
  constructor(name: string) { this.name = name; }
  @loggedMethod
  greet() { console.log(`Hello, my name is ${this.name}.`); }
}

const p = new Person("Ray");
p.greet();

The first parameter is the method being decorated; the second is a context object providing metadata such as kind, name, and addInitializer.

Decorator Context

Different decorator kinds have distinct context types: ClassDecoratorContext, ClassMethodDecoratorContext, ClassGetterDecoratorContext, ClassSetterDecoratorContext, ClassFieldDecoratorContext, and ClassAccessorDecoratorContext. The context definition includes readonly kind: string, readonly name: string, and addInitializer(initializer: () => void): void.

addInitializer (New Syntax)

addInitializer

allows code to run before the class constructor (or static initialization) executes.

function classDecorator(target: any, context: ClassDecoratorContext) {
  context.addInitializer(() => {
    console.log('classDecorator addInitializer here', target);
  });
}

function staticFieldDecorator(target: any, context: ClassFieldDecoratorContext) {
  context.addInitializer(() => {
    console.log('staticFieldDecorator addInitializer here', target);
  });
}

function staticMethodDecorator(target: any, context: ClassMethodDecoratorContext) {
  context.addInitializer(() => {
    console.log('staticMethodDecorator addInitializer here', target);
  });
}

function instanceFieldDecorator(target: any, context: ClassFieldDecoratorContext) {
  context.addInitializer(() => {
    console.log('instanceFieldDecorator addInitializer here', target);
  });
}

function instanceMethodDecorator(target: any, context: ClassMethodDecoratorContext) {
  context.addInitializer(() => {
    console.log('instanceMethod addInitializer here', target);
  });
  function replacementMethod(this: any, ...args: any[]) {
    const result = target.call(this, ...args);
    return result;
  }
  return replacementMethod;
}

@classDecorator
class Person {
  @staticFieldDecorator
  static age: number = 23;

  @staticMethodDecorator
  static run() { console.log('run'); }

  constructor(name: string) { console.log('constructor'); this.name = name; }

  @instanceFieldDecorator
  name: string = 'Forest';

  @instanceMethodDecorator
  eat() { console.log('eat sth'); }
}

const p = new Person('Ray');

Difference from Experimental Decorators

Previous versions required the --experimentalDecorators flag. The new standard decorators are not compatible with the old ones or with metadata reflection.

Writing Fully Typed Decorators

Fully typed decorators often need many generics, which can affect readability. Further documentation will be provided.

Const Type Parameters

The const modifier can be applied to generic type parameters, eliminating the need for as const assertions.

function getConstValue<T>(arg: T): T { return arg; }
const names = getConstValue(["Jack", "Bob", "Eve"]); // inferred as readonly ["Jack","Bob","Eve"]
const result = getConstValue({ name: 'Jack', frd: ["Bob","Eve"] }); // inferred as readonly properties
// Errors if mutated
names[0] = '1'; // error
result.frd[0] = 'Sophia'; // error

The const modifier works with generic constraints as well.

Using with Generic Constraints

declare function fnBad<const T extends string[]>(args: T): void; // T inferred as string[]
fnBad(["a","b","c"]);

declare function fnGood<const T extends readonly string[]>(args: T): void; // T inferred as readonly tuple
fnGood(["a","b","c"]);

Scope of Const Modifier

The inference works only when the argument is a literal expression; passing a variable does not trigger the const inference.

compilerOptions "extends" Supports Multiple Files

{
  "compilerOptions": {
    "extends": ["./tsconfig1.json", "./tsconfig2.json"]
  }
}

Enums

Before 5.0, enums were split into numeric and string enums. TypeScript 5.0 unifies them into "union enums" where the enum type is the union of all member types.

Before vs. After

5.0 before: enum members used as types required literal initializers.

5.0 after: this restriction is removed.

String enums can now have computed members.

Numeric Enum Initialization

Previously, a numeric constant could not be used without an explicit initializer for the following member. In 5.0, the rule is relaxed; the next member auto‑increments.

enum E { a = num, b } // b = num + 1 works in 5.0

Open Question

When using numeric constants or constant expressions, are the members considered "computed" or "constant"? The documentation currently treats them as computed, but they also satisfy constant‑member constraints.

moduleResolution "bundler" Support

TypeScript 4.7 added node16 and nodenext to better support ESM in Node.js, but they impose strict file‑extension requirements. The new bundler option combines ESM and CommonJS resolution strategies without those limitations.

Related moduleResolution Options

allowImportingTsExtensions

When enabled (with --noEmit and --emitDeclarationOnly), imports may include .ts, .mts, and .tsx extensions.

resolvePackageJsonExports & resolvePackageJsonImports

These options make TypeScript respect the exports and imports fields in a package’s package.json, supporting conditional exports.

allowArbitraryExtensions

Allows importing files with non‑standard extensions; TypeScript looks for a matching {basename}.d.{ext}.ts declaration file.

customConditions

Enables user‑defined conditions for conditional exports, e.g., "my-condition".

verbatimModuleSyntax

Simplifies the interaction of importsNotUsedAsValues, preserveValueImports, and isolatedModules.

Support for export type *

Type‑only re‑exports are now allowed via export * from "module" and export * as ns from "module".

JSDoc @satisfies Support

TypeScript 4.9 introduced the satisfies operator; JSDoc now supports @satisfies for JavaScript type checking.

JSDoc @overload Support

Multiple signatures for a function can be expressed in JSDoc using @overload.

tsc Build Flags

The build mode now supports --declaration, --emitDeclarationOnly, --declarationMap, --sourceMap, and --inlineSourceMap.

Case‑Insensitive Import Sorting in Editors

TypeScript now defaults to case‑sensitive sorting, reducing conflicts with tools like ESLint. Unstable options can be configured via typescript.unstable settings such as organizeImportsIgnoreCase, organizeImportsCollation, and related locale options.

Improved Switch/Case Completion

Switch/Case Completion GIF
Switch/Case Completion GIF

Speed, Memory, and Bundle Size Optimizations

Version 5.0 brings substantial performance gains: building VS Code with the Beta took only 81% of the time compared to 4.9, and the package size dropped from 63.8 MB to 37.3 MB (58%). Improvements include moving TypeScript to ES modules, monomorphizing nodes and symbols, reducing identifier sizes, caching printer output, and limiting var usage.

References

Announcing TypeScript 5.0 – devblogs.microsoft.com/typescript/announcing-typescript-5-0/

Announcing TypeScript 5.0 Beta – devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/

Enjoyed the article? Follow us for more updates! 💁🏼‍♀️

TypeScriptEnumdecoratorsmoduleResolutioncompiler optionsconst assertions
Alipay Experience Technology
Written by

Alipay Experience Technology

Exploring ultimate user experience and best engineering practices

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.