Stop Hand-Writing the Env Interface for Workers
For a while I kept an Env interface next to my wrangler.jsonc and updated both by hand. It drifted, obviously. A binding renamed in the config stayed the old name in the type, and TypeScript happily agreed with me right up until the request failed.
Wrangler generates the whole thing:
wrangler typesThat writes worker-configuration.d.ts containing an Env built from your actual bindings, plus the runtime types for the Workers global scope. It replaces @cloudflare/workers-types entirely, and it’s aware of your compatibility_date and flags, so the surface it describes is the surface you’ll get.
Point tsconfig at it and delete the package:
{ "compilerOptions": { "types": ["./worker-configuration.d.ts"] }}It’s a build step, so treat it like one
The file is only correct as of the last run. Add a binding, forget to regenerate, and you’re back to hand-maintained types with extra steps.
{ "scripts": { "cf-typegen": "wrangler types", "postinstall": "wrangler types" }}Running it on install means a fresh clone and a teammate’s new binding both work without anyone remembering anything.
In Astro
The Cloudflare adapter exposes bindings through Astro.locals.runtime, which needs one declaration to connect to the generated Env:
type Runtime = import('@astrojs/cloudflare').Runtime<Env>;
declare namespace App { interface Locals extends Runtime {}}After that, Astro.locals.runtime.env.MY_KV autocompletes in a .astro file, which still feels slightly unreasonable.
One habit to keep: don’t edit the generated file. It’s large, it’s overwritten on every run, and anything you add there disappears the next time somebody adds a queue.
