TypeScript 5.7: New Features for Developers

The new TypeScript 5.7 release introduces significant improvements aimed at increasing flexibility and reducing repetitive work during development. In this article, we will explore TypeScript new features and understand how they can enhance our code with practical examples.


1. Improved Conditional Types: Writing More Flexible Code

Conditional types in TypeScript allow you to define dynamic types, meaning types that change based on specific conditions. TypeScript 5.7 enhances this functionality, making it easier and more precise to write code.

Practical Example:

type IsString<T> = T extends string ? "It is a string" : "It is not a string";

type Test1 = IsString<string>;  // "It is a string"
type Test2 = IsString<number>;  // "It is not a string"

Added Value

  • Reduces errors related to type management.
  • Increases code readability, especially in complex projects.
  • Allows you to create more robust logic directly within types without additional code.

2. Support for Decorators: Adding Functionality Without Modifying Existing Code

To use decorators, you need to enable the experimentalDecorators option in the tsconfig.json file.

Minimum Configuration for tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

Decorators are an experimental feature that allows adding functionality to methods and classes without rewriting them. This is particularly useful for operations such as logging, data validation, or authorization management.

Practical Example:

function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Method ${propertyKey} called with arguments: ${JSON.stringify(args)}`);
    return originalMethod.apply(this, args);
  };
}

class Calculator {
  @Log
  add(a: number, b: number) {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(2, 3); // Console: "Method add called with arguments: [2, 3]"

Added Value

  • Eliminates repetitive code, keeping the project cleaner.
  • Ideal for applying common behaviors (such as logging or error handling) in a modular way.
  • Facilitates the creation of scalable and maintainable architectures.

3. New rewriteRelativeImportExtensions Option: Automatic File Extension Management

The Problem: Manual File Extension Handling

When writing TypeScript code, .ts or .tsx files are compiled into .js files. However, in relative imports (import from local files), TypeScript does not automatically rewrite file extensions, leaving imports as .ts. This can create problems in the compiled JavaScript code.
For example, if you write an import like this:

// myModule.ts
import { myFunction } from './utils/helper.ts';  // .ts is not valid in JavaScript

Once compiled, the JavaScript file retains the .ts extension, causing a runtime error because Node.js or the browser does not recognize files with the .ts extension.


The Solution: rewriteRelativeImportExtensions

With TypeScript 5.7, you can configure the compiler to automatically rewrite file extensions from .ts, .tsx, .mts, and .cts to .js, .mjs, or .cjs in JavaScript output files. This ensures that imports are always valid in the final code.

Configuration in tsconfig.json:

{
  "compilerOptions": {
    "module": "ESNext",
    "rewriteRelativeImportExtensions": true
  }
}

Complete Practical Example

Before TypeScript 5.7 (Problem)

// utils/helper.ts
export function greet(name: string) {
  return `Hello, ${name}!`;
}

// main.ts
import { greet } from './utils/helper.ts';  // This import will NOT work in JavaScript
console.log(greet('TypeScript'));

Compiled JavaScript Code:

// main.js
import { greet } from './utils/helper.ts';  // Error! Node.js does not recognize .ts extension
console.log(greet('TypeScript'));

Runtime Error: Cannot find module './utils/helper.ts'

After TypeScript 5.7 (Solution)

With the new rewriteRelativeImportExtensions option, TypeScript automatically changes .ts to .js in the compiled code.

Unchanged TypeScript Code:

// main.ts
import { greet } from './utils/helper.ts';
console.log(greet('TypeScript'));

Compiled JavaScript Code with rewriteRelativeImportExtensions:

// main.js
import { greet } from './utils/helper.js';  // Correct! The extension is now .js
console.log(greet('TypeScript'));

Result: No error, the code runs correctly in the JavaScript runtime.


Added Value

  • Prevents runtime errors caused by invalid imports.
  • Reduces manual work, especially in projects with many relative imports.
  • Improves compatibility with Node.js, Deno, and ES Module-based environments.

4. Checks for Never-Initialized Variables

The Problem: Uninitialized Variables in Classes

Before TypeScript 5.7, TypeScript did not warn about class properties that were declared but never initialized. This could lead to runtime errors when trying to access properties that were undefined.

The Solution in TypeScript 5.7

Now, TypeScript checks if a class property is never initialized and reports an error.

Example Before TypeScript 5.7 (No Error, But Risk of Runtime Crash)

class User {
  name: string;  // ❌ This did not generate an error, but could cause runtime issues
}

const u = new User();
console.log(u.name.length);  // ⚠️ Runtime error: Cannot read properties of undefined

Example in TypeScript 5.7 (Error Reported)

class User {
  name: string;  // ❌ Error: Property 'name' has no initializer and is not definitely assigned in the constructor.
}

The compiler now warns that name is not initialized and could lead to problems.

How to Fix the Issue

1️⃣ Initialize the Variable Directly

class User {
  name: string = "Default Name";  // ✅ No error
}

2️⃣ Initialize the Variable in the Constructor

class User {
  name: string;
  
  constructor(name: string) {
    this.name = name;  // ✅ No error
  }
}
class User {
  name!: string;  // ⚠️ Disables the error, but use with caution
}

This option disables the check, so use it only if you are sure the variable will be initialized before use.

Benefits of This Feature

✅ Prevents runtime errors from uninitialized variables.
✅ Makes code safer and more reliable.
✅ Helps detect potential bugs before execution.


5. Performance Improvements: Faster Compilation

In addition to new features, TypeScript 5.7 includes optimizations that reduce compilation times and improve the developer experience. Less waiting means more time to focus on code.


Conclusion

TypeScript 5.7 offers useful new features to make code more flexible, secure, and easy to maintain. The improvements to conditional types and support for decorators are valuable tools that can simplify developers’ daily work.

Upgrade to version 5.7 to take full advantage of these new features and check out the official documentation for further details.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top