@clementvial

Developer from Canada 🇨🇦
Product, infrastructure, AI, and web3.
Mostly on AWS and Cloudflare.

All notes

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 types

That 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:

tsconfig.json
{
"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.

package.json
{
"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:

src/env.d.ts
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.