Skip to content

cloudflare-* templates: type vending DX silently breaks without manual cf-typegen #120

Description

@ashunar0

Summary

After PR #90 migrated the cloudflare-* templates from @cloudflare/workers-types to wrangler's dynamic wrangler types (which I think was the right call — config-aware types are genuinely more accurate), users who rely on Hono's type vending features hit immediate type errors after scaffolding, because worker-configuration.d.ts doesn't exist until wrangler types is manually run.

The README mentions cf-typegen, but missing this step results in silent breakage that's hard to diagnose, and easy to skip.

Reproduction

Using cloudflare-workers+vite (same applies to cloudflare-workers and cloudflare-pages):

pnpm create hono@latest hono-temp
# → choose cloudflare-workers+vite
cd hono-temp && pnpm install
pnpm add -D typescript

Replace src/index.tsx with a minimal type-vending example:

import { Hono } from 'hono'
import type { ExtractSchema } from 'hono/types'

const app = new Hono().get('/api/users', (c) => c.json({ users: [] }))

type AppSchema = ExtractSchema<typeof app>
const schemaCheck: { '/api/users': unknown } = {} as AppSchema

export default app

Run typecheck:

$ pnpm exec tsc --noEmit
src/index.tsx:8:7 - error TS2741: Property '"/api/users"' is missing in type 'BlankSchema'
  but required in type '{ "/api/users": unknown; }'.

Hono's chain inference produces BlankSchema because no global Response/Request type is available in the project — tsconfig.json has only lib: ["ESNext"] and types: ["vite/client"].

Fix verification

$ pnpm cf-typegen
✨ Types written to worker-configuration.d.ts

$ pnpm exec tsc --noEmit
(no output, exit 0)

Once worker-configuration.d.ts is generated, type vending works as expected.

Why this matters

Real-world users who would hit this:

  • Anyone using hc<typeof app>() RPC client → client types resolve to unknown
  • Anyone using @hono/inertia's PageProps<'Foo'> → fails with Type 'X' does not satisfy the constraint 'never'
  • Anyone running tsc --noEmit in CI → CI fails on a fresh scaffold

For comparison, sibling templates handle this seamlessly out-of-the-box:

  • bun: @types/bun in devDependencies
  • nodejs / vercel: @types/node + types: ["node"]
  • cloudflare-*: nothing — relies on the user knowing to run cf-typegen

Possible approaches

I understand the rationale for PR #90 (config-aware types via wrangler types is more accurate), so I'm not suggesting reverting it. Instead, options to close the UX gap:

A. Add postinstall to template package.json

"scripts": {
  "postinstall": "wrangler types --env-interface CloudflareBindings"
}

Pros:

  • Covers initial install, teammate clones, CI environments, and wrangler.jsonc updates
  • Works across all package managers (npm/pnpm/yarn/bun)
  • Smallest change — template-only, no create-hono CLI changes

Cons:

  • postinstall failures abort install (e.g., on Node < 22)

B. Run wrangler types from create-hono CLI after install

Add a hook that runs wrangler types for cloudflare templates after install.

Pros:

  • Keeps package.json clean

Cons:

  • Doesn't help when teammates clone and run pnpm install
  • Doesn't help when wrangler.jsonc changes
  • Requires changes to create-hono itself

C. Hybrid: A + predev/prebuild

Combine A with predev/prebuild to also keep types fresh on every dev/build run.


Personally I lean toward A as the simplest fix with the broadest coverage. Happy to open a PR once direction is decided.

Side note: an additional UX gotcha during reproduction — wrangler types itself requires Node 22+. Users on Node 20 hit a secondary error (Wrangler requires at least Node.js v22.0.0) when manually running cf-typegen. Worth keeping in mind for the postinstall approach (postinstall would surface this earlier).

cc @yusukebe @Thomascogez (referencing PR #90 context)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions