Skip to content

TypeScript: @fedify/cfworkers WorkersKvStore nominal type mismatch persists with wrangler/vite-generated types (follow-up to #662) #665

@SJang1

Description

@SJang1

Summary

Issue #662 fixed the @cloudflare/workers-types/experimental import. In v2.1.4, the Queue type mismatch is resolved, but WorkersKvStore still causes a TypeScript error for projects using wrangler-generated types (wrangler types / @cloudflare/vite-plugin).

The generated worker-configuration.d.ts re-declares KVNamespace under a different module path. TypeScript treats these as nominally distinct types even though they are structurally identical.

Affected versions

  • @fedify/cfworkers: 2.1.4
  • wrangler: 4.80.0
  • @cloudflare/vite-plugin: 1.30.0
  • @cloudflare/workers-types: 4.20260405.1

Error example

server/worker/federation/fedify.ts(26,28): error TS2345:
  Argument of type 'KVNamespace<string>' is not assignable to parameter of type
  'import(".../node_modules/@cloudflare/workers-types/index").KVNamespace<string>'.
    Types of property 'get' are incompatible.

Note: WorkersMessageQueue(env.QUEUE_BINDING) compiles cleanly in v2.1.4. Only WorkersKvStore(env.KV_BINDING) still errors.

Root cause

There are two sources of Cloudflare runtime types:

Source Loaded via Module path
@cloudflare/workers-types (npm) @fedify/cfworkers dependency node_modules/@cloudflare/workers-types/index
worker-configuration.d.ts (generated) wrangler types or @cloudflare/vite-plugin local file

Both define structurally identical KVNamespace, but TypeScript's module-scoped type identity means KVNamespace from source A is not assignable to KVNamespace from source B.

This is not an experimental vs standard entrypoint issue (that was #662). This is a package types vs generated types identity mismatch that affects any project using wrangler types or @cloudflare/vite-plugin for type generation.

Why #662's fix doesn't fully cover this case

The fix in #662 (commit 7b2bdff) changed the import from @cloudflare/workers-types/experimental to the standard @cloudflare/workers-types entrypoint. This helps projects that configure their tsconfig to include @cloudflare/workers-types directly (both sides share the same module path), and fully resolved the Queue type mismatch.

However, KVNamespace still mismatches because Cloudflare's recommended workflow (wrangler types) generates a standalone worker-configuration.d.ts that re-declares KVNamespace with a different module identity.

Proposed fix: use a structural (duck-typed) interface for WorkersKvStore

Instead of importing the concrete KVNamespace type from @cloudflare/workers-types:

// current (2.1.4)
import { KVNamespace } from "@cloudflare/workers-types";

class WorkersKvStore implements KvStore {
  constructor(namespace: KVNamespace<string>) { ... }
}

Define a minimal structural interface for what the code actually uses:

// proposed
interface KVNamespaceLike {
  getWithMetadata<Metadata = unknown>(
    key: string,
    options: KVNamespaceGetOptions<"json">
  ): Promise<KVNamespaceGetWithMetadataResult<unknown, Metadata>>;
  put(key: string, value: string, options?: KVNamespacePutOptions): Promise<void>;
  delete(key: string): Promise<void>;
  list<Metadata = unknown>(options?: KVNamespaceListOptions): Promise<KVNamespaceListResult<Metadata>>;
}

class WorkersKvStore implements KvStore {
  constructor(namespace: KVNamespaceLike) { ... }
}

This approach:

  • Accepts any object that structurally matches (wrangler-generated, @cloudflare/workers-types, or runtime)
  • Could eliminate @cloudflare/workers-types as a peer dependency for KV
  • Follows the same pattern Fedify uses elsewhere (e.g. KvStore, MessageQueue are structural interfaces)
  • Matches what was apparently already done for Queue (which now works without @ts-expect-error)

Current workaround

// @ts-expect-error — @fedify/cfworkers imports KVNamespace from @cloudflare/workers-types; wrangler/vite generates its own nominal KVNamespace
kv: new WorkersKvStore(env.FEDIFY_KV),

Related

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions