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.
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)
addInitializerallows 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'; // errorThe 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.0Open 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
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! 💁🏼♀️
Alipay Experience Technology
Exploring ultimate user experience and best engineering practices
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
