{}const=>[]async()letfn</>var
DevelopmentWeb

TypeScript 6: What's new and how changes break old code

In this guide for beginner developers, we will analyze all the breaking changes of TypeScript 6.0 with real code examples: what will break, why it happened and how to fix it in 5 minutes.

К

Kodik

Author

7 min read

TypeScript 6.0 is not just another update with a couple of new features. It transition release, which sets the stage for the revolutionary TypeScript 7.0 with a tenfold increase in speed. But with new features come changes that can break your existing code.

Why is TypeScript 6.0 a special release?

The TypeScript team is working on a complete rewrite of the compiler to native code (Go language), which will give a 7-10x performance boost. TypeScript 7.0 with a native compiler is expected in 2026, and TypeScript 6.0 serves as a bridge between the current version and the future.

This means that in version 6.0:

  • New language features will appear

  • Some old settings will become deprecated

  • The default behavior for many options will change

  • Very old features that conflict with the future will be removed

🔥 100,000+ students already with us

Tired of reading theory?
Time to code!

Kodik — an app where you learn to code through practice. AI mentor, interactive lessons, real projects.

🤖 AI 24/7
🎓 Certificates
💰 Free
🚀 Start learning
Joined today

The main new feature: resource management through using.

The most notable innovation in TypeScript 6.0 is the using keyword for explicit resource management (Explicit Resource Management). This solves one of the most common problems of developers: forgotten connections to databases, uncleaned event handlers and memory leaks.

How did it work before?

❌ Old way

async function fetchUserData(userId: string) {
    const db = await connectToDatabase();
    const cache = new RedisConnection();
    
    try {
        const user = await db.users.findById(userId);
        await cache.set(`user:${userId}`, user);
        return user;
    } finally {
        // Don't forget to close the connections
        await db.close();
        await cache.close();
    }
}

Problem: it is easy to forget to close the connection, especially if there are many ways out of the function.

✅ New way with using

async function fetchUserData(userId: string) {
    using db = await connectToDatabase();
    using cache = new RedisConnection();
    
    // Automatically cleared when exiting the function
    const user = await db.users.findById(userId);
    await cache.set(`user:${userId}`, user);
    return user;
    // db and cache will automatically close here
}

Solution: Resources are automatically cleared when you exit the scope!

Where is it useful?

🗄️ Connections to the database: PostgreSQL, MongoDB, Redis will automatically close

📁 File operations: files will be closed after reading/writing

⚛️ React components: automatic cleaning of subscriptions and listeners

🔒 Any cleanup operations: temporary files, locks, transactions

Improved type inference.

TypeScript 6.0 has become smarter at understanding context and inferring types. This is especially noticeable when working with:

  • Promises and async/await: the compiler better understands the chains of asynchronous operations

  • Generics: you don't need to explicitly specify the types — TypeScript will guess itself

  • Conditional types: more accurate definition of types in complex scenarios

TypeScript 5.x

// Required explicit type indication
const items = await Promise.all([
    fetch('/api/users').then(r => r.json() as User[]),
    fetch('/api/posts').then(r => r.json() as Post[])
]);

TypeScript 6.0

// Displays types automatically
const items = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json())
]);
// items already have the correct type!

Breaking Changes: What will break in your code?

And now the most important thing — changes that can break the existing code. Let's look at each one in detail.

1. --strict will become the default

What's changing: Strict type checking mode will be enabled by default.

What does this mean?

  • strictNullChecks — you cannot assign null or undefined without explicit permission

  • noImplicitAny — you can't leave the any type implicitly

  • strictFunctionTypes — more strict function type checking

❌ How the code breaks:

// It used to work
function getUser(id: number) {
    return users.find(u => u.id === id); // returns User | undefined
}

const user = getUser(1);
console.log(user.name); // Error in TS 6.0! user may be undefined

✅ Correct in TS 6.0:

const user = getUser(1);
if (user) {
    console.log(user.name); // OK
}
// or
console.log(user?.name); // OK, optional chaining

How to fix: Add checks to null/undefined or use optional chaining (?.).

2. Deletion --target es5

What's changing: It will not be possible to compile in ES5. Minimum — ES2015 (ES6).

Why: ES5 is JavaScript from 2009. Modern browsers and Node.js have long supported ES6+. ES5 support slows down the compiler.

// tsconfig.json
{
    "compilerOptions": {
        "target": "es5" // ❌ Error in TypeScript 6.0!
    }
}
// tsconfig.json
{
    "compilerOptions": {
        "target": "es2015" // ✅ Minimum ES2015
    }
}

How to fix:

  • Change target to "es2015" or newer

  • If you need support for older browsers, use Babel for transpilation after TypeScript

3. Changes in --moduleResolution

What's changing: Old module resolution strategies are becoming obsolete:

  • --moduleResolution node (or node10) — to be removed

  • New recommended: bundler, node16, nodenext

⚠️ Attention: Imports may stop working if you use relative paths without extensions.

It used to work

// From --moduleResolution node
import { helper } from './utils';

Now you need

// Explicitly specify the extension
import { helper } from './utils.js';
// Yes, .js even for .ts files!

💡 Why .js for .ts files?

TypeScript follows the ES Modules standard, where imports must be the same as in the final JavaScript.

How to fix:

  1. Change moduleResolution in tsconfig.json

  2. Add extensions to imports

  3. Or use "bundler" — it is more flexible

4. Deleting --baseUrl

What's changing: The --baseUrl option is deprecated and will be removed.

Old way

// tsconfig.json
{
    "compilerOptions": {
        "baseUrl": "./src" // ❌ Outdated!
    }
}

// Imports worked like this
import { Button } from 'components/Button';

New method

// tsconfig.json
{
    "compilerOptions": {
        "paths": {
            "@components/*": ["./src/components/*"],
            "@utils/*": ["./src/utils/*"]
        }
    }
}

// Now imports look like this
import { Button } from '@components/Button';

5. Changing the default value of types

What's changing: The types field will default to an empty array [] instead of automatically including all types from node_modules/@types.

// It used to work automatically
import * as express from 'express'; // types were picked up automatically
// In TS 6.0, you need to explicitly specify
{
    "compilerOptions": {
        "types": ["node", "express", "jest"]
    }
}

💡 Why it's useful:

Many projects accidentally included hundreds of unused types, which slowed down the compilation by 20-50%.

6. rootDir default = tsconfig.json directory

What's changing: rootDir is no longer calculated automatically from the file structure.

// Before TS 6.0: rootDir was calculated from the sources
// src/
//   app/
//     index.ts
//   utils/
//     helper.ts

// After TS 6.0: rootDir = directory with tsconfig.json
// You need to explicitly specify
{
    "compilerOptions": {
        "rootDir": "./src"
    }
}

7. The asserts keyword is no longer supported

BREAKING

What's changing: The keyword asserts was experimental and is now being removed because it was added and removed from the JavaScript specification.

// ❌ This no longer works
import json from './data.json' 
    asserts { type: 'json' };
// ✅ Use instead
import json from './data.json';

How to prepare for migration to TypeScript 6.0

Step 1: Check your tsconfig.json

Find the outdated settings:

{
    "compilerOptions": {
        // ❌ Delete/replace
        "target": "es5",
        "moduleResolution": "node",
        "baseUrl": "./src",
        
        // ✅ Add explicitly if needed
        "strict": false, // If you want to postpone the inclusion of strict
        "types": ["node", "jest"], // Specify explicitly
        
        // ✅ New recommendations
        "target": "es2015",
        "moduleResolution": "bundler",
        "paths": {
            "@/*": ["./src/*"]
        }
    }
}

Step 2: Update imports

If you use moduleResolution: "node16" or "nodenext", add the extensions:

// Before
import { helper } from './utils';

// Now
import { helper } from './utils.js';

Step 3: Enable strict gradually

If you are not ready for the full strict mode, turn on the options one by one:

{
    "compilerOptions": {
        "strict": false,
        "strictNullChecks": true, // Start with this
        // Then add the rest as you are ready
        // "noImplicitAny": true,
        // "strictFunctionTypes": true,
    }
}

Step 4: Test on TypeScript 5.9

TypeScript 5.9 already shows warnings that it will become obsolete in 6.0. Use it to prepare:

npm install -D typescript@5.9

Performance: why it matters

Although the main speed increase will come in TypeScript 7.0, there are already optimizations in 6.0:

Caching of intermediate types: When working with complex libraries (Zod, tRPC), compilation is accelerated

📦 Fewer files are checked: Thanks to the change types by default

🚀 More efficient module resolution: New strategies faster

And in TypeScript 7.0 (which will be released after 6.0):

  • Compilation is faster in 10 times

  • Memory usage reduced by 2 times

  • Loading a project in the editor with 9.6 seconds to 1.2 seconds

Conclusion.

TypeScript 6.0 is an important step in the evolution of the language. Yes, it will bring breaking changes, but they are all aimed at:

  • 🛡️ Increasing code security (through strict)

  • ⚡ Improved performance (through optimization and preparation for TS 7.0)

  • 🎯 Compliance with modern standards (through updating the modular system)

The using keyword solves a real resource management problem. Improved type inference makes the code cleaner. And preparation for TypeScript 7.0 promises a revolutionary increase in speed.

Yes, migration will require effort, especially if you have a large project. But the result is worth it: safer, faster and more modern code.

This and much more can be learned in Codice!

We analyze everything in detail — from the basics to advanced concepts — and consolidate knowledge with practical tasks.

And if you need help or want to discuss the code — we already have more 2000 like-minded people in active telegram channel, where they will always help and advise! 🚀

🎯Stop procrastinating

Liked the article?
Time to practice!

In Kodik, you don't just read — you write code immediately. Theory + practice = real skills.

Instant practice
🧠AI explains code
🏆Certificate

No registration • No card