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
}
}
3️⃣ Use the !
Operator (Not Recommended Unless Necessary)
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.