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 typestatements.
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:
.tsfiles are treated as ESM by default- Relative imports require
.jsextensions (TypeScript resolves them to.tsfiles) 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
- Update to the latest TypeScript 5.x first
- Fix all deprecation warnings
- Update to TypeScript 7
- Fix any new errors (usually a handful for most projects)
The Feature Checklist
Here's everything new in TypeScript 7, in one place:
| Feature | Chapter | Impact |
|---|---|---|
| Pattern Matching | 17 | 🔴 High — changes how you handle variants |
| TC39 Decorators | 18 | 🔴 High — stable metaprogramming |
| Const Type Parameters | 19 | 🟡 Medium — eliminates as const boilerplate |
using Declarations | 20 | 🟡 Medium — automatic resource cleanup |
| Inline Type Imports | 20 | 🟢 Low — cleaner import syntax |
| ESM by Default | 20 | 🟡 Medium — modern module defaults |
| Parallel Type Checking | 20 | 🟡 Medium — faster builds |
Improved in Narrowing | 20 | 🟢 Low — better type inference |
| Better Error Messages | 20 | 🟢 Low — nicer developer experience |
Try This: TypeScript 7 Features
- Write a class with a
Symbol.disposemethod and useusingto create an instance. Verify it's disposed when it goes out of scope. - Use inline type imports (
import { type User, createUser }) in a file. - Enable
parallel: truein your tsconfig and compare build times. - Use
satisfieswith a generic function to validate a config object while preserving literal types. - Try
noUncheckedIndexedAccesswith.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.