Skip to main content

By the end of this chapter, you'll have a complete map of every significant feature in TypeScript 7. Not just the headliners — pattern matching, decorators, const type parameters — but the smaller features that make your life better every day. The ones you might miss if you don't know to look for them.

Think of this chapter as the TypeScript 7 changelog, rewritten for humans. No jargon. No RFC references. Just: here's what's new, here's why it matters, here's when to use it.

using Declarations: Resource Management​

The using keyword is TypeScript 7's implementation of the TC39 Explicit Resource Management proposal. It ensures that resources are cleaned up when they go out of scope — no more forgotten close() calls, no more leaked file handles, no more dangling database connections.

The Problem​

// Without 'using' — easy to forget cleanup
function processFile(path: string) {
const file = fs.openSync(path, "r");
try {
const data = file.read();
return processData(data);
} finally {
file.close(); // Easy to forget!
}
}

The Solution​

// With 'using' — cleanup is automatic
function processFile(path: string) {
using file = fs.openSync(path, "r");
const data = file.read();
return processData(data);
// file is automatically closed when it goes out of scope
}

using works with any object that implements the Symbol.dispose or Symbol.asyncDispose method:

class DatabaseConnection {
[Symbol.dispose]() {
this.close();
console.log("Connection closed");
}
}

function query() {
using db = new DatabaseConnection();
return db.query("SELECT * FROM users");
// db is automatically disposed here
}

Async Disposal​

For async resources, use await using:

class AsyncResource {
async [Symbol.asyncDispose]() {
await this.cleanup();
}
}

async function processData() {
await using resource = new AsyncResource();
// ... use resource
// resource is automatically disposed when it goes out of scope
}

When to Use using​

  • File handles
  • Database connections
  • Network sockets
  • Locks and mutexes
  • Any resource that needs explicit cleanup

Type-Only Imports 2.0​

TypeScript 7 improves type-only imports with inline syntax:

// Old way: separate import statements
import type { User } from "./user";
import { createUser } from "./user";

// TypeScript 7: inline type imports
import { type User, createUser } from "./user";

The type keyword before an individual import tells TypeScript (and your bundler) to erase that import from the compiled output. It's cleaner than separate import type statements and works with any import syntax:

// Default imports
import { type default as User, createUser } from "./user";

// Renamed imports
import { type User as UserType, createUser } from "./user";

// Namespace imports
import type * as Types from "./types"; // All type-only
import * as UserModule from "./user"; // All value

Why This Matters​

  • Smaller bundles. Type-only imports are erased, reducing bundle size.
  • Faster builds. Bundlers can skip type-only imports entirely.
  • Cleaner code. No need for separate import type statements.

ESM and Module Resolution Improvements​

TypeScript 7 makes ES module support the default:

module: "nodenext" Is Now the Default​

New TypeScript 7 projects default to module: "nodenext" and moduleResolution: "nodenext". This means:

  • .ts files are treated as ESM by default
  • Relative imports require .js extensions (TypeScript resolves them to .ts files)
  • package.json "type": "module" is respected

Better CJS/ESM Interop​

TypeScript 7 handles CommonJS/ESM interop more gracefully:

// Importing a CJS module in ESM
import express from "express"; // Works without esModuleInterop
import * as express from "express"; // Also works

.mts and .cts Extensions​

TypeScript 7 fully supports .mts (module TypeScript) and .cts (CommonJS TypeScript) extensions:

src/
├── index.mts # Always treated as ESM
├── legacy.cts # Always treated as CommonJS
└── shared.ts # Depends on package.json "type"

Compiler Performance​

TypeScript 7 is significantly faster than previous versions:

Incremental Parsing​

The compiler now caches parse results between runs. If you change one file, only that file is re-parsed. This makes tsc --watch feel instant.

Parallel Type Checking​

TypeScript 7 can use multiple CPU cores for type checking. Enable it with:

{
"compilerOptions": {
"parallel": true
}
}

For large projects, this can cut type-checking time in half.

Faster Declaration Emit​

Generating .d.ts files is now 2-3x faster, thanks to optimized declaration emit logic.

Smaller but Significant Features​

Improved in Narrowing​

TypeScript 7 narrows types more aggressively with the in operator:

function process(value: { name: string } | { age: number }) {
if ("name" in value) {
// TypeScript 7: value is { name: string }
// Previous: value is { name: string } | { age: number } (no narrowing in some cases)
console.log(value.name);
}
}

satisfies with Generics​

The satisfies operator now works with generic types:

function createConfig<const T>(config: T) {
return config;
}

const config = createConfig({
host: "localhost",
port: 5432,
}) satisfies Record<string, string | number>;
// config.host: "localhost" (literal preserved!)
// config.port: 5432 (literal preserved!)

Better Error Messages​

TypeScript 7 improves error messages with:

  • Contextual suggestions: "Did you mean 'email'?" now works in more cases
  • Multi-line errors: Complex type mismatches are formatted across multiple lines for readability
  • Quick-fix links: Some errors include links to documentation

noUncheckedIndexedAccess Improvements​

The noUncheckedIndexedAccess flag now works with more patterns:

const arr = [1, 2, 3];
const first = arr[0]; // number | undefined (with noUncheckedIndexedAccess)

// TypeScript 7: also works with .at()
const last = arr.at(-1); // number | undefined

// And with destructuring
const [a, b] = arr; // a: number | undefined, b: number | undefined

Breaking Changes (And How to Handle Them)​

TypeScript 7 has a few breaking changes from TypeScript 5.x:

1. Stricter any Checking​

Functions returning any are now flagged more aggressively:

// TypeScript 7: Warning — this function returns 'any'
function parseJSON(json: string) {
return JSON.parse(json);
}

// Fix: annotate the return type
function parseJSON(json: string): unknown {
return JSON.parse(json);
}

2. experimentalDecorators Removed​

The old experimental decorators flag is removed. Migrate to the new TC39 decorators (Chapter 18).

3. Stricter Module Resolution​

Some import patterns that worked with moduleResolution: "node" may not work with "nodenext". The fix is usually adding .js extensions to relative imports.

Migration Path​

  1. Update to the latest TypeScript 5.x first
  2. Fix all deprecation warnings
  3. Update to TypeScript 7
  4. Fix any new errors (usually a handful for most projects)

The Feature Checklist​

Here's everything new in TypeScript 7, in one place:

FeatureChapterImpact
Pattern Matching17🔴 High — changes how you handle variants
TC39 Decorators18🔴 High — stable metaprogramming
Const Type Parameters19🟡 Medium — eliminates as const boilerplate
using Declarations20🟡 Medium — automatic resource cleanup
Inline Type Imports20🟢 Low — cleaner import syntax
ESM by Default20🟡 Medium — modern module defaults
Parallel Type Checking20🟡 Medium — faster builds
Improved in Narrowing20🟢 Low — better type inference
Better Error Messages20🟢 Low — nicer developer experience

Try This: TypeScript 7 Features​

  1. Write a class with a Symbol.dispose method and use using to create an instance. Verify it's disposed when it goes out of scope.
  2. Use inline type imports (import { type User, createUser }) in a file.
  3. Enable parallel: true in your tsconfig and compare build times.
  4. Use satisfies with a generic function to validate a config object while preserving literal types.
  5. Try noUncheckedIndexedAccess with .at() and array destructuring.

Time needed: 20 minutes.

What to notice: How using eliminates manual cleanup. How inline type imports clean up import statements. How parallel type checking speeds up large projects. How the small features add up to a significantly better development experience.

The Bridge​

You now know everything that's new in TypeScript 7. But knowing the features isn't the same as knowing how to USE them in production. How do you combine pattern matching, decorators, and const type parameters into a coherent architecture? How do you structure a real application with these tools?

That's the next chapter. Production patterns. The real-world techniques that separate TypeScript developers from TypeScript masters.


In the next chapter: branded types, opaque types, Zod integration, API type safety, discriminated union reducers, and the patterns that make production TypeScript codebases a joy to work with.