Skip to content

TypeScript Integration

ABCrimson edited this page Mar 11, 2026 · 2 revisions

TypeScript Integration

modern-cmdk leverages TypeScript 6.0.1-rc features for maximum type safety.

Branded Types

Item and group IDs are branded to prevent accidental string mixing:

import { itemId, groupId } from 'modern-cmdk';

const id = itemId('copy');    // ItemId (branded string)
const gid = groupId('actions'); // GroupId (branded string)

// Type error: string is not assignable to ItemId
machine.send({ type: 'ITEM_SELECT', id: 'copy' });

// Correct:
machine.send({ type: 'ITEM_SELECT', id: itemId('copy') });

NoInfer

Prevents unwanted type widening in callbacks:

// The `query` parameter won't influence type inference of `T`
filter: (item: CommandItem, query: NoInfer<string>) => boolean

Disposable Protocol

All stateful objects implement Disposable:

{
  using machine = createCommandMachine({ ... });
  // Use the machine
} // Automatically disposed here — subscriptions cleaned, state reset

{
  await using engine = await createWasmSearchEngine();
  // Use the WASM engine
} // WASM memory freed automatically

Isolated Declarations

All exports have explicit type annotations (isolatedDeclarations: true), enabling parallel .d.ts generation.

erasableSyntaxOnly

No enum, no namespace, no parameter properties. Only erasable TypeScript syntax is used, enabling direct TS execution via --experimental-strip-types.

Clone this wiki locally