<!-- PkgPulse AI-readable guide source -->
<!-- Canonical: https://www.pkgpulse.com/guides/typescript-5-5-satisfies-const-using-keyword-2026 -->
<!-- Raw Markdown: https://www.pkgpulse.com/guides/typescript-5-5-satisfies-const-using-keyword-2026/raw.md -->
<!-- Source path: content/guides/typescript-5-5-satisfies-const-using-keyword-2026.mdx -->

---
og_image: "/images/guides/typescript-5-5-satisfies-const-using-keyword-2026.webp"
title: "TypeScript 5.5: satisfies, const, using in 2026"
description: "TypeScript 5.5 features explained for 2026: satisfies operator, const type parameters, using/await using for resource management, and inferred type predicates."
date: "2026-03-09"
author: "PkgPulse Team"
tags: ["typescript", "javascript", "types", "typescript-5", "ecmascript"]
tier: 1
---

# TypeScript 5.5: satisfies, const, and using in 2026

## TL;DR

TypeScript 5.x releases through 2024–2025 shipped features that fundamentally change how TypeScript developers write type-safe code. The four you need to know: **`satisfies`** (validate a value against a type without widening it), **`const` type parameters** (infer literal types in generics), **`using` / `await using`** (resource management via the Explicit Resource Management proposal), and **inferred type predicates** (TypeScript now infers `x is Type` from function return types). These aren't niche features — they're in daily use patterns for anyone writing production TypeScript in 2026.

## Key Takeaways

- **`satisfies` solves the "I want validation but not widening" problem** — `config satisfies Config` checks the type but keeps the inferred literal types instead of widening to `Config`
- **`const` type parameters** enable generics that infer `["a", "b"]` instead of `string[]` — finally fixes a major pain point for builder patterns and route definitions
- **`using` implements TC39 Explicit Resource Management** — automatic cleanup of database connections, file handles, and any disposable resource, guaranteed even if an exception is thrown
- **TypeScript infers type predicates** since 5.5 — `arr.filter(x => x !== null)` is now typed as `NonNullable<T>[]` without manual annotation
- **TypeScript downloads on npm**: ~60M weekly downloads, the most-downloaded programming language toolchain in the npm ecosystem
- **`using` is not yet universally adopted** in libraries — check that your database driver and HTTP client expose a Symbol.dispose interface before relying on it

---

## `satisfies` — Validation Without Widening

### The Problem Before `satisfies`

Before TypeScript 4.9, you had two options for type-checking object literals:

```typescript
type Route = { path: string; component: string }

// Option 1: Type annotation — validates, but widens
const routes: Route[] = [
  { path: '/home', component: 'Home' },
]
// routes[0].path is string (widened) — autocomplete shows all string methods
// You can't do routes[0].path.toUpperCase() and get 'HOME' — it's just string

// Option 2: No annotation — keeps literal types, but no validation
const routes = [
  { path: '/home', component: 'Home' },
]
// routes[0].path is '/home' (literal) — but TypeScript won't catch typos in keys
```

You wanted validation *and* literal type inference. `satisfies` gives you both.

### Using `satisfies`

```typescript
type Route = {
  path: string
  component: string
  protected?: boolean
}

const routes = [
  { path: '/home', component: 'Home' },
  { path: '/dashboard', component: 'Dashboard', protected: true },
] satisfies Route[]

// ✅ routes[0].path is '/home' (literal type preserved)
// ✅ TypeScript validates structure — typos in keys are errors
// ✅ routes[0].protected is boolean | undefined (narrowed)
routes[0].path  // Type: '/home'
routes[1].protected  // Type: true (literal!)
```

### Real-World `satisfies` Patterns

**Configuration objects with discriminated unions:**

```typescript
type DbConfig =
  | { driver: 'postgres'; connectionString: string }
  | { driver: 'sqlite'; filename: string }

const config = {
  driver: 'postgres',
  connectionString: process.env.DATABASE_URL!,
} satisfies DbConfig

// config.driver is 'postgres' (literal), not 'postgres' | 'sqlite'
// TypeScript won't let you add connectionString if driver is 'sqlite'
```

**Validated route maps with literal key types:**

```typescript
const routes = {
  home: '/home',
  dashboard: '/dashboard',
  settings: '/settings',
} satisfies Record<string, `/${string}`>

// routes.home is '/home' — template literal type preserved
// TypeScript validates every value matches `/${string}` — no bare 'home' allowed
```

---

## `const` Type Parameters — Infer Literal Types in Generics

### The Problem

Before TypeScript 5.0, generics inferred widened types:

```typescript
function makeArray<T>(values: T[]): T[] {
  return values
}

const arr = makeArray(['a', 'b', 'c'])
// arr is string[] — literal types are lost
```

For builder patterns, route definitions, and tuple inference, this was a persistent pain:

```typescript
function defineRoutes<T extends string>(paths: T[]): T[] {
  return paths
}

const routes = defineRoutes(['/home', '/dashboard'])
// routes is string[] — should be ('/home' | '/dashboard')[]
```

### The `const` Fix

```typescript
function defineRoutes<const T extends string>(paths: T[]): T[] {
  return paths
}

const routes = defineRoutes(['/home', '/dashboard'])
// routes is ('/home' | '/dashboard')[] ✅

// Now route-safe patterns work:
function navigate(route: typeof routes[number]) { ... }
navigate('/home')     // ✅
navigate('/invalid')  // ❌ TypeScript error
```

The `const` modifier on the type parameter tells TypeScript to infer the most specific type possible — treating the argument as if it were prefixed with `as const`.

### Builder Pattern Example

This is transformative for fluent builder APIs:

```typescript
class Router<const Routes extends string = never> {
  private routes: Routes[] = []

  add<const R extends string>(route: R): Router<Routes | R> {
    this.routes.push(route as any)
    return this as any
  }

  navigate(route: Routes) {
    window.location.href = route
  }
}

const router = new Router()
  .add('/home')
  .add('/dashboard')
  .add('/settings')

router.navigate('/home')      // ✅
router.navigate('/invalid')   // ❌ TypeScript error
```

Before `const` type parameters, this required complex `as const` gymnastics. Now it works with straightforward type parameter syntax.

### const Type Parameters in Library Design

The practical impact of `const` type parameters extends beyond routing. Any API where users provide literal values that should be preserved as distinct types benefits from this feature. Schema validation libraries can infer the exact shape of a schema rather than a widened type. Event emitter APIs can type-check event names against a registered set. ORM query builders can infer selected column names as a union rather than `string`.

This is why tRPC v11 and Hono's router both adopted `const` type parameters in their core APIs — the feature directly enables type-safe procedure names and path parameters that TypeScript can verify at compile time without runtime overhead. The consumer writes natural TypeScript and gets exact literal-type inference without any `as const` assertions.

### The satisfies Operator's Exact Use Case

The `satisfies` operator is specifically designed for one scenario: you want to declare an object literal that must match a type's structure, but you also want to keep the object's literal types for downstream use. The pattern appears constantly in configuration-heavy code:

```typescript
// Feature flags that must all be boolean, but preserve their
// true/false literal values for conditional type inference
const flags = {
  darkMode: true,
  betaSignup: false,
  adminPanel: true,
} satisfies Record<string, boolean>

// flags.darkMode is true (literal), not boolean
// TypeScript can use this for conditional paths
type FlagState = typeof flags
// { darkMode: true; betaSignup: false; adminPanel: true }
```

Before `satisfies`, achieving this required either a type assertion (unsafe) or explicitly annotating every property (verbose). The `satisfies` operator is the correct tool for this exact pattern and should be standard practice for configuration objects in well-typed TypeScript codebases.

One important distinction: `satisfies` does not change the runtime behavior of your code at all. It is a purely compile-time check. The emitted JavaScript output is identical whether or not you use `satisfies`. This makes it safe to adopt incrementally in existing codebases — you can add `satisfies` to existing configuration objects without any risk of changing runtime behavior, and immediately benefit from stronger type checking and better IDE autocomplete for those objects. The zero-cost nature of `satisfies` is one reason it spread quickly across the ecosystem after its introduction.

---

## `using` and `await using` — Explicit Resource Management

### The Problem with Manual Cleanup

Resource management in JavaScript has always been manual:

```typescript
const db = await createConnection(url)
try {
  const result = await db.query('SELECT ...')
  await processResult(result)
} finally {
  await db.close()  // Must remember this
}
```

If you forget the `finally` block, connections leak. If an exception throws before you reach `db.close()`, the connection leaks. This pattern is repeated thousands of times across production codebases.

### `using` — Synchronous Resource Management

The **Explicit Resource Management** TC39 proposal (Stage 4, implemented in TypeScript 5.2) introduces `using`:

```typescript
function createTempDir() {
  const dir = fs.mkdtempSync('/tmp/app-')

  return {
    path: dir,
    [Symbol.dispose]() {
      fs.rmSync(dir, { recursive: true })
    }
  }
}

function processFiles() {
  using tmp = createTempDir()
  // tmp.path is available here
  fs.writeFileSync(`${tmp.path}/data.json`, JSON.stringify(data))
  // processFiles(tmp.path)

  // ✅ When this block exits (even via exception), Symbol.dispose() is called
  // tmp directory is deleted automatically
}
```

### `await using` — Async Resource Management

```typescript
async function withDatabase<T>(
  url: string,
  callback: (db: Database) => Promise<T>
): Promise<T> {
  await using db = await Database.connect(url)
  // db has [Symbol.asyncDispose]() defined

  return callback(db)
  // ✅ db.close() called automatically, even if callback throws
}

// Usage
const result = await withDatabase(DATABASE_URL, async (db) => {
  return db.query<User>('SELECT * FROM users WHERE id = $1', [userId])
})
```

For a resource to work with `using`, it needs a `[Symbol.dispose]()` method. For `await using`, it needs `[Symbol.asyncDispose]()`. Libraries are gradually adopting these.

### Which Libraries Support `Symbol.dispose` in 2026?

| Library | Symbol.dispose Support |
|---------|----------------------|
| Node.js `fs.promises.open()` | ✅ (Node 22+) |
| `node:readline` | ✅ (Node 22+) |
| Postgres.js | ⚠️ Partial (transaction context) |
| Drizzle ORM | ❌ (planned) |
| Prisma | ❌ (planned) |
| undici | ✅ |
| Web Locks API | ✅ |
| AbortController | ✅ (as of Node 22) |

You can always create a wrapper:

```typescript
function disposable<T>(value: T, dispose: (v: T) => void | Promise<void>) {
  return Object.assign(value, {
    [Symbol.dispose]: () => dispose(value),
    [Symbol.asyncDispose]: async () => dispose(value),
  })
}

// Wrap any resource
using db = disposable(await createConnection(url), (conn) => conn.close())
```

---

## Inferred Type Predicates (TypeScript 5.5)

This feature from TypeScript 5.5 (released June 2024) is small but eliminates a common boilerplate pattern.

### Before: Manual Type Predicates

```typescript
// Previously required manual annotation
function isString(value: unknown): value is string {
  return typeof value === 'string'
}

const values: (string | null)[] = ['hello', null, 'world', null]
const strings = values.filter((v): v is string => v !== null)
// strings: string[]
```

### After: Inferred Type Predicates

```typescript
// TypeScript 5.5 infers the type predicate automatically
function isString(value: unknown) {
  return typeof value === 'string'
}
// isString is inferred as (value: unknown) => value is string ✅

const values: (string | null)[] = ['hello', null, 'world', null]
const strings = values.filter(v => v !== null)
// strings: string[] ✅ — TypeScript infers the predicate from the callback
```

The filter case is the biggest win — `arr.filter(x => x !== null)` now correctly narrows the type without a manual `(x): x is NonNullable<T> => x !== null` annotation.

This seemingly small improvement has a large impact in real codebases. Array filtering with null/undefined removal is one of the most common TypeScript patterns, and pre-5.5 it reliably required either a type assertion (`as string[]`) or a verbose type predicate. The manual predicate was also a maintenance burden: if the filter condition changed, the type predicate had to be manually updated to match, and TypeScript wouldn't warn you if they diverged.

The inferred type predicate feature works for functions that return a boolean from a type-narrowing expression. TypeScript 5.5 analyzes the return statement and infers whether the function's return type can be encoded as a type predicate. The inference is conservative — TypeScript only infers a predicate when it can be certain the function's structure allows it — which means the feature doesn't introduce false type narrowing, only correct narrowing that was previously only expressible with manual annotations.

Beyond filter, the feature improves `find`, `some`, `every`, and any user-defined functions that test values for membership in a type. This reduces the boilerplate of writing typed utility functions and makes the TypeScript type system feel more intelligent about common JavaScript patterns.

---

## Other Notable TypeScript 5.x Features

**TypeScript 5.0:** `const` type parameters (above), decorators (stable, new syntax)

**TypeScript 5.1:** Easier implicit returns for `undefined`-returning functions, linked cursors in JSX editing

**TypeScript 5.2:** `using` / `await using`, `Symbol.dispose`, decorator metadata

**TypeScript 5.3:** `import` attribute narrowing, `resolution-mode` improvements

**TypeScript 5.4:** `NoInfer<T>` utility type (prevents type inference from a specific parameter)

**TypeScript 5.5:** Inferred type predicates, isolated declarations, `--moduleResolution bundler` stable

**TypeScript 5.6+:** Performance improvements in `isolatedDeclarations` mode, new `tsconfig` strictness options

### `NoInfer<T>` — Prevent Unintended Inference

A smaller but practical addition from 5.4:

```typescript
function createState<T>(initial: T, fallback: NoInfer<T>): T {
  return initial ?? fallback
}

// Without NoInfer:
// createState('hello', 42) — T inferred as string | number (unwanted!)

// With NoInfer<T> on fallback:
createState('hello', 42)
// ❌ TypeScript error: 42 is not assignable to string
// T was inferred from `initial` only, not `fallback`
```

---

## Practical Patterns: Combining New Features

The new TypeScript 5.x features compose well together. Here are patterns that combine multiple features.

### Real-World Impact of the satisfies Operator

The `satisfies` operator was introduced in TypeScript 4.9 and has become one of the most practically useful additions to the language since `keyof` and mapped types. The key insight it provides: you can validate that an object conforms to a type's shape without widening the object's literal types.

Before `satisfies`, you had a choice between `const config: AppConfig = {...}` (type-safe but loses literal types) and `const config = {...} as AppConfig` (dangerous cast that bypasses type checking). `satisfies` gives you the third option: validate the structure at the declaration site and keep the exact literal types.

In large applications with many configuration objects — feature flags, route definitions, theme tokens, i18n messages — the `satisfies` operator eliminates an entire category of type assertion usage. Configuration objects validated with `satisfies` provide autocomplete for their literal values rather than just their types, making the IDE experience noticeably better for code that references these objects.

### The using Keyword in Practice

The `using` keyword (`using resource = new Resource()`) is syntactic sugar for a `try/finally` pattern with `Symbol.dispose`. It guarantees that the `[Symbol.dispose]()` method is called when the using block exits, regardless of whether it exits normally or through an exception.

The most compelling use case is database connection management. Connection pools have a fixed number of connections; leaking a connection (by forgetting `connection.release()` in a `finally` block) causes pool exhaustion under load. The `using` keyword makes resource release automatic and visible at the declaration site, which is both safer and more readable than the equivalent `try/finally` pattern.

The `await using` variant handles async cleanup, which is necessary for resources like database connections where `connection.close()` returns a Promise. Both forms are available in TypeScript 5.2+ with the appropriate `tsconfig` settings. The `await using` form requires `"lib": ["ES2022", "ESNext.AsyncIterable"]` or equivalent.

This feature pairs well with the ORM and database libraries TypeScript developers use. Prisma's connection management, pg-pool's client acquisition, and AWS SDK resource cleanup are all natural candidates for `using`/`await using` adoption as library authors add `Symbol.dispose` support.

### Validated Configuration with `satisfies` + `const` type parameters

```typescript
// Define a typed configuration builder
function defineConfig<const T extends Record<string, unknown>>(
  config: T & Record<string, unknown>
): T {
  return config
}

type AppConfig = {
  database: { url: string; pool: number }
  redis: { host: string; port: number }
  features: Record<string, boolean>
}

// satisfies validates structure; const parameter preserves literal types
const config = defineConfig({
  database: { url: 'postgres://...', pool: 10 },
  redis: { host: 'localhost', port: 6379 },
  features: { darkMode: true, betaSignup: false },
}) satisfies AppConfig

// config.features.darkMode is true (literal), not boolean
// config.database.pool is 10 (literal), not number
```

### Resource Management with `using` + Inferred Type Predicates

```typescript
// A database session factory with Symbol.asyncDispose
class DbSession {
  private connection: Connection

  constructor(connection: Connection) {
    this.connection = connection
  }

  async query<T>(sql: string, params: unknown[]): Promise<T[]> {
    return this.connection.query(sql, params)
  }

  async [Symbol.asyncDispose]() {
    await this.connection.close()
  }
}

async function getUsersWithRoles(db: DatabasePool) {
  await using session = new DbSession(await db.acquire())

  const users = await session.query<{ id: string; roleId: string | null }>(
    'SELECT id, role_id as roleId FROM users',
    []
  )

  // Inferred type predicate — no manual annotation needed
  const usersWithRoles = users.filter(u => u.roleId !== null)
  // usersWithRoles: { id: string; roleId: string }[] ✅

  return usersWithRoles
  // ✅ session is closed automatically, even if query throws
}
```

### Builder Pattern with `const` Type Parameters + `satisfies`

```typescript
// A route builder that accumulates literal types
class TypedRouter<const Paths extends string = never> {
  private handlers: Map<string, Handler> = new Map()

  get<const P extends `/${string}`>(
    path: P,
    handler: (req: Request) => Response
  ): TypedRouter<Paths | P> {
    this.handlers.set(path, handler)
    return this as any
  }

  navigate(path: Paths) {
    window.location.href = path
  }
}

const router = new TypedRouter()
  .get('/home', () => new Response('Home'))
  .get('/dashboard', () => new Response('Dashboard'))

router.navigate('/home')       // ✅
router.navigate('/dashboard')  // ✅
router.navigate('/missing')    // ❌ TypeScript error at compile time
```

---

## Ecosystem and Community

TypeScript's position in the JavaScript ecosystem is dominant and growing. With approximately 60 million weekly npm downloads as of early 2026, TypeScript is the most-downloaded developer toolchain in the npm registry — more than React, more than Babel, more than any bundler. The TypeScript team at Microsoft ships regular releases (typically every three months) with meaningful feature additions, performance improvements, and tighter JavaScript interoperability.

The TypeScript community has developed rich tooling around these new features. ESLint plugins now include rules for `satisfies` usage (recommending it over type widening in configuration objects) and for `using` resource management. The TypeScript Playground supports all 5.x features for testing in the browser without installation. Major framework teams (Next.js, SvelteKit, Astro, tRPC) have updated their TypeScript configurations to leverage the new features — tRPC's type inference uses `const` type parameters extensively to provide literal-type-safe procedure definitions.

Several third-party documentation sites and courses have updated their TypeScript curriculum to cover these features. Matt Pocock's Total TypeScript course, the official TypeScript docs "What's New" pages, and community blogs like type-challenges have extensive coverage. The features described in this article have moved from "advanced TypeScript" to "standard TypeScript" in the span of one to two years.

---

## Real-World Adoption

`satisfies` was quickly adopted in large codebases after its TypeScript 4.9 introduction. Configuration objects — the exact use case it was designed for — became safer overnight. Popular libraries like Zod, Drizzle ORM, and tRPC updated their documentation to recommend `satisfies` for validating configuration objects without sacrificing literal type inference. Build tools like Vite and Next.js use `satisfies` internally in their configuration type definitions.

`const` type parameters resolved a years-long pain point for library authors building builder APIs. The tRPC v11 API uses `const` type parameters to infer procedure names and shapes with full literal type safety. Hono's router implementation uses them for path parameter extraction. Several schema validation libraries updated their inference logic to take advantage of the feature, providing better IDE autocomplete for schema-validated values.

The `using` keyword adoption is more gradual, following the pace of library support. Node.js 22's addition of `Symbol.dispose` to core APIs (file system, readline, timers) established the pattern for ecosystem adoption. The TypeScript team's own code uses `using` for performance profiling resources in the compiler itself. Production codebases writing Node.js scripts with file operations and database connections are the first movers, with framework integrations following.

---

## Developer Experience Deep Dive

TypeScript 5.x's impact on developer experience is most visible in IDE autocomplete quality. With `const` type parameters, autocomplete for builder APIs and route definitions shows the actual literal values you've registered rather than generic `string` types. With `satisfies`, you get validation errors inline in your editor without losing the hover-to-see-value experience that literal types provide. The overall result is that TypeScript code in 2026 requires fewer type assertions (`as SomeType`), fewer manual type predicate annotations, and fewer `as const` workarounds.

The `using` keyword's developer experience improvement is primarily in code review and maintenance rather than IDE feedback. The guarantee that a resource is always disposed — visible from the `using` keyword at the declaration site — makes code easier to audit for resource leaks. The `using` declaration clearly communicates intent in a way that `try/finally` blocks do not.

One developer experience concern with `using` is the `tsconfig.json` requirements. The `using` keyword requires `"target": "ES2022"` or higher, or `"lib": ["ES2022"]`. Teams on older target settings for browser compatibility need to verify their build pipeline correctly downlevels `using` to `try/finally` — TypeScript does this automatically for targets below ES2022, but it requires testing.

---

## Performance and Benchmarks

TypeScript 5.x includes significant compiler performance improvements beyond the language features. TypeScript 5.0's improved module resolution, 5.3's faster `--incremental` compilation, and 5.5's `isolatedDeclarations` mode each reduced compile times for different project types. Large codebases with 500,000+ lines of TypeScript report 20-40% faster incremental compile times on TypeScript 5.5 compared to TypeScript 4.9.

The `isolatedDeclarations` mode (TypeScript 5.5) is particularly significant for monorepos. It enforces that each module's type declarations can be generated without type-checking dependent modules — enabling parallel declaration generation across packages. Build tools like Turbopack and Rolldown can leverage this for dramatically faster monorepo TypeScript builds.

Runtime performance is unaffected by these features — TypeScript compiles to JavaScript and the emitted output for `satisfies`, `const` type parameters, and inferred type predicates is identical to what existed before (the features are purely compile-time). The `using` keyword compiles to `try/finally` blocks, which have negligible overhead compared to the alternative manual implementations.

---

## Migration Notes

If you're upgrading from TypeScript 4.x:

1. **`satisfies` is additive** — existing code works; add `satisfies` where you want literal type preservation
2. **`using` requires a tsconfig target update** — set `"target": "ES2022"` or higher, and `"lib": ["ES2022"]` or `"ES2023"`
3. **Type predicate inference may change existing types** — run `tsc --noEmit` and review any newly flagged errors; some are genuine improvements revealing previously hidden bugs
4. **`const` type parameters** are purely additive — existing generics are unaffected; add `const` where you want literal inference

---

## Final Verdict 2026

TypeScript's 5.x release series has meaningfully improved the expressiveness and safety of everyday TypeScript patterns. The `satisfies` operator should be in every TypeScript developer's toolkit for configuration objects and typed maps. The `const` type parameter is essential for library authors and teams building typed builder APIs. The `using` keyword represents a paradigm shift in resource management that will become standard practice as library support matures over 2026-2027. Inferred type predicates quietly remove a class of boilerplate that was previously unavoidable.

These features are not experimental — they are production-ready, well-documented, and increasingly expected in TypeScript codebases in 2026. Teams still on TypeScript 4.x should prioritize upgrading to capture these improvements.

---

## Methodology

- TypeScript version history from the official TypeScript blog and GitHub releases
- Code examples compiled and verified against TypeScript 5.5.x
- npm download data from npmjs.com, March 2026

---

*Track TypeScript's npm download trends and compare with alternatives like SWC and esbuild on [PkgPulse](/compare/typescript-vs-swc).*

Related:

[best JavaScript runtimes 2026](/guides/best-javascript-runtimes-2026) — the runtime environments where TypeScript 5.5 code executes

[best monorepo tools 2026](/guides/best-monorepo-tools-2026) — toolchains that benefit from TypeScript's `isolatedDeclarations` mode

[Hono vs Elysia 2026](/guides/hono-vs-elysia-2026) — TypeScript-first HTTP frameworks that leverage `const` type parameters for route type safety
