Guide
Convert an OpenAPI spec into TypeScript types — without a generator
Most front-end projects don't need a full OpenAPI client. They need three or four types. Here's the lighter path.
Need it now? OpenAPI → TypeScript converter.
Step 1 — Find the schema you actually need
Most teams discover later they only really cared about a handful of types. Open the spec and copy just the relevant chunk:
openapi: 3.0.3
info:
title: Demo
version: 1.0.0
paths: {}
components:
schemas:
User:
type: object
required: [id, name]
properties:
id: { type: integer }
name: { type: string }
email: { type: string, format: email }
addresses:
type: array
items: { $ref: "#/components/schemas/Address" }
Address:
type: object
required: [line1, country]
properties:
line1: { type: string }
city: { type: string }
country: { type: string }Step 2 — Generate TypeScript interfaces
Paste the spec (JSON or YAML) into the OpenAPI → TypeScript converter. You'll get one named interface per components.schemas entry, with $ref turned into proper type references:
export interface Address {
"line1": string;
"city"?: string;
"country": string;
}
export interface User {
"id": number;
"name": string;
"email"?: string;
"addresses"?: Address[];
}Step 3 — Use the types in fetch
Annotate the return type so your IDE flags any field-level drift the moment the backend changes:
import type { User } from "./types";
export async function fetchUser(id: number): Promise<User> {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<User>;
}Step 4 — Wire into React Query / SWR
Pass the generated type as the query result type. data is now fully typed throughout your component tree:
import { useQuery } from "@tanstack/react-query";
import type { User } from "./types";
export function useUser(id: number) {
return useQuery<User>({
queryKey: ["user", id],
queryFn: () => fetch(`/api/users/${id}`).then((r) => r.json()),
});
}Step 5 — Decide if you want runtime validation
TypeScript types disappear at runtime. If you want to fail fast when the backend ships an unexpected shape (especially in B2B integrations), pair the types with a Zod schema:
// Want runtime validation as well? Convert the same schema to Zod
// instead — see /guides/json-to-zod and /openapi-to-zod.
import { User } from "./schemas";
const u = User.parse(await fetch("/api/me").then(r => r.json()));Schemato has OpenAPI → Zod for that. Use whichever fits your trust model.
Common pitfalls
- • YAML indentation gotchas.Tabs break parsing — use 2-space indentation. Schemato's built-in YAML parser handles the OpenAPI subset, not arbitrary YAML 1.2.
- • External $ref isn't resolved. Only same-document references work. Inline external schemas first or use a bundler like
@redocly/cli bundle. - • Discriminated unions need manual cleanup.
oneOfwith discriminator becomes a generic union — tighten with"kind": "card"string literal types. - • Don't over-generate. If you only need 3 types, paste only those 3 schemas. Generating the whole spec encourages dependency drift.
FAQ
Both are great for full clients. This is a lighter path: one paste, no install, no codegen step in CI.
Yes — same-document $ref into components.schemas becomes named interfaces.
Both. Schemato includes a lightweight YAML parser sufficient for the OpenAPI subset.
No — only types. Use openapi-fetch if you need wrappers; often raw fetch is enough.