types
Generate the voidhash.gen.d.ts declaration file from the server schema and verify it stays in sync in CI.
voidhash-cli types keeps your project's generated TypeScript declaration file —
voidhash.gen.d.ts — in sync with the schema on the server. It has two subcommands:
types generate— fetch the server schema and write the declaration file (optionally watching for changes)types check— CI gate that fails when the committed file has fallen behind the server
Both require a logged-in session (auth login) and a voidhash.config.ts in the
project root (init). You get a clear error pointing at the right command when
either is missing.
types generate
npx voidhash-cli types generateFetches the schema from the server and writes the declaration file, then prints the embedded version:
Fetching remote schema...
✓ Types written to voidhash.gen.d.ts (version 3f6c1a9e2b74d0c8e5a...)The output path defaults to voidhash.gen.d.ts at the project root; override it with
typesOutput in voidhash.config.ts:
import { defineConfig } from "voidhash-cli";
export default defineConfig({
team: "my-team",
project: "my-app",
typesOutput: "src/voidhash.gen.d.ts", // optional
});Every generated file starts with a @voidhash:version header carrying the server-computed schema
version hash:
// voidhash.gen.d.ts — generated by voidhash-cli, do not edit
// @voidhash:version 3f6c1a9e2b74d0c8e5a1...
// @voidhash:fetched-at 2026-07-17T09:30:00.000Z
declare module "@voidhash/react-native" {
interface VoidhashRegister {
schema: {
products: "monthly_sub" | "yearly_sub";
locations: "onboarding" | "settings-upsell";
perks: "all-access" | "premium-features";
};
}
}
export {};The version is supplied by the server, not computed locally, so client and server can never
disagree on the hash. It is what --watch and types check compare against.
Commit the generated file
voidhash.gen.d.ts belongs in version control — that is what makes types reproducible across
your team and what gives types check something to verify in CI.
Flags
--watch— after the initial generate, keep running and regenerate whenever the server schema changes. Defaultfalse.--poll-interval-ms <n>— polling interval for--watchmode, in milliseconds. Default5000.
Watch mode
npx voidhash-cli types generate --watch --poll-interval-ms 2000Watch mode polls the server's lightweight GET /schema/version endpoint on the configured
interval and only re-downloads the full schema — and rewrites the file — when the version hash
actually changes. Press Ctrl+C to stop.
The loop is deliberately resilient: poll errors and regeneration failures are logged as warnings and skipped, so a flaky network or a momentary logout never kills the watcher. You keep working against the last-known-good declaration file.
types check
npx voidhash-cli types checkThe CI gate. Reads the @voidhash:version header from the local declaration file and compares it
against the server's current schema version:
- In sync — prints
✓ Types are up to date (version ...)and exits0. - Stale — prints the divergence and exits non-zero, so a stale committed file cannot pass PR CI:
✗ Types are stale.
Local: 3f6c1a9e2b74d0c8e5a1...
Server: 9d2e7b41f0a3c6d8e2b5...
Run 'voidhash-cli types generate' to refresh, then commit the updated declaration file.Two failure modes get distinct errors so they are easy to diagnose in CI logs:
- File missing —
Could not read generated types at voidhash.gen.d.ts. Run 'voidhash-cli types generate' first. - Header missing —
voidhash.gen.d.ts is missing the @voidhash:version header. Re-run 'voidhash-cli types generate' to regenerate.(usually the result of hand-editing the file)
CI workflow example
Authenticate the CLI in CI by setting the API key from a secret (the same api_key that
auth login stores in ~/.voidhash on your machine — see config), then run
the check:
name: voidhash types
on: pull_request
jobs:
types-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npx voidhash-cli config set api_key "$VOIDHASH_API_KEY"
env:
VOIDHASH_API_KEY: ${{ secrets.VOIDHASH_API_KEY }}
- run: npx voidhash-cli types checkWhen someone renames a product slug in the dashboard, the next PR fails types check until the
declaration file is regenerated and committed — at which point TypeScript surfaces every call site
still using the old slug.
What the generated file powers
The React Native SDK exports an intentionally empty VoidhashRegister interface; the generated
file augments it via declaration merging. ProductSlug, LocationSlug, and PerkSlug resolve
against the augmentation, so hooks like usePaywallByLocation("onboarding") autocomplete the
literal union of valid slugs. Without the file (or with an empty schema), they degrade gracefully
to string — the file is purely a compile-time aid; nothing at runtime depends on it.
During development you rarely run the watcher by hand: the SDK's Metro plugin spawns
voidhash-cli types generate --watch alongside expo start and tears it down on shutdown:
const { getDefaultConfig } = require("expo/metro-config");
const { withVoidhash } = require("@voidhash/react-native/metro");
module.exports = withVoidhash(getDefaultConfig(__dirname), {
pollIntervalMs: 5000, // optional
});The plugin's pollIntervalMs option is forwarded to --poll-interval-ms. See
Schema & Generated Types for the full picture of how the
server schema, the runtime fetch, and the generated types fit together.
Related
- Schema & Generated Types — how the SDK consumes the declaration file
- init — creates
voidhash.config.tsand the initialvoidhash.gen.d.ts - auth — login session required by both subcommands
- config — profiles and non-interactive
api_keysetup for CI