手動でTypeScriptインターフェースを書くのをやめよう - JSON、CSV、OpenAPIから一つのコマンドで生成
原題: Stop writing TypeScript interfaces by hand - generate them from JSON, CSV, and OpenAPI in one command
分析結果
- カテゴリ
- IT
- 重要度
- 56
- トレンドスコア
- 18
- 要約
- TypeScriptのインターフェースを手動で作成するのは時間がかかりますが、JSON、CSV、OpenAPIから一つのコマンドで自動生成する方法があります。このアプローチにより、開発者は効率的にインターフェースを作成でき、エラーを減らし、コードの整合性を保つことができます。
- キーワード
You're integrating a third-party API. You open the docs, find a sample response, and start typing: interface Product { id : number ; name : string ; price : number ; // wait, is currency a string or an object? // what's the shape of variants[]? // does stock ever go null? } Twenty minutes later you have a fragile interface you're not fully confident in — and the moment the API ships a new field, it's already stale. There's a better way. snaptype: point it at the source, get types back snaptype is a CLI that generates TypeScript interfaces and Zod schemas by reading your actual data — live API endpoints, OpenAPI specs, JSON files, CSV files. No config. No code to write. One command. from-url — types straight from a live endpoint This is the one I use most. Give it any JSON endpoint: npx snaptype from-url https://api.example.com/products --zod -o src/types/product.ts snaptype fetches the response and generates: export interface Product { id : number ; name : string ; price : number ; currency : " USD " | " EUR " | " GBP " ; stock : number | null ; createdAt : string ; variants : Variant []; } export interface Variant { sku : string ; color : string ; size : " S " | " M " | " L " | " XL " ; } export const ProductSchema = z . object ({ id : z . number (), name : z . string (), price : z . number (), currency : z . enum ([ " USD " , " EUR " , " GBP " ]), stock : z . number (). nullable (), createdAt : z . iso . datetime (), variants : z . array ( VariantSchema ), }); A few things worth noticing: Nested objects are expanded — variants[] became its own Variant interface automatically Enums are inferred — currency and size had a small set of repeated values, so snaptype made them union types instead of string Nullables are detected — stock was null in some responses, so it became number | null Semantic types — createdAt became z.iso.datetime() , not a generic z.string() This is inference against real data, not a naive field-by-field mapping. Real-world example: GitHub API Point it at any real endpoint — here the GitHub user API: npx snaptype from-url https://api.github.com/users/torvalds --zod -o src/types/github-user.ts Done. You now have typed access to every field in that response — including nested objects — with Zod schemas you can use directly for runtime validation. When GitHub ships a new field, re-run the command. No manual diffing. from-openapi — one file per schema, from any spec If the API you're integrating has an OpenAPI or Swagger spec (most do), this is even better. Point it at the spec file: npx snaptype from-openapi ./openapi.yaml -o src/types/ Or directly at the hosted spec URL: npx snaptype from-openapi https://petstore3.swagger.io/api/v3/openapi.json -o src/types/ snaptype reads every schema definition in the spec and generates one typed file per schema. YAML and JSON both work. For a spec that defines Pet , Order , and User , you get three files: // src/types/Pet.ts export interface Pet { id ?: number ; name : string ; photoUrls : string []; status ?: " available " | " pending " | " sold " ; tags ?: Tag []; category ?: Category ; } // src/types/Order.ts export interface Order { id ?: number ; petId ?: number ; quantity ?: number ; shipDate ?: string ; status ?: " placed " | " approved " | " delivered " ; complete ?: boolean ; } Before, setting up a typed client from an OpenAPI spec meant: Finding (or writing) a code generator Configuring it Running it Cleaning up the generated mess Updating it every time the spec changes Now it's one command. Works with private and authenticated APIs One-off request — pass headers directly with -H : npx snaptype from-url https://api.example.com/me \ -H "Authorization: Bearer $TOKEN " \ --zod -o src/types/me.ts Multiple endpoints on the same API — store the headers in .snaptyperc so you don't repeat yourself: { "baseUrl" : "https://api.example.com" , "headers" : { "Authorization" : "Bearer my-token" , "X-Tenant" : "acme" } } Then every from-url call picks them up automatically: npx snaptype from-url /users -o src/types/user.ts npx snaptype from-url /products -o src/types/product.ts npx snaptype from-url /orders -o src/types/order.ts Headers set in .snaptyperc are defaults — anything passed with -H on the CLI takes priority if the same key appears in both. stdin fallback — if the endpoint requires a flow you can't replicate in a single request (custom auth, cookies, proxy), pipe the response in from curl: curl -s -H "Authorization: Bearer $TOKEN " https://api.example.com/me \ | npx snaptype from-stdin --zod -o src/types/me.ts JSON files and CSV too If you're working from a local file rather than a live endpoint: # Local JSON file npx snaptype from-json ./user.json --zod -o src/types/user.ts # CSV export from a database or spreadsheet npx snaptype from-csv ./orders.csv --zod -o src/types/order.ts The inference is the same — enums, nullables, semantic types, nested objects. Zero config to start No setup needed. If you want to pin defaults, add a .snaptyperc : { "zod" : true , "outDir" : "src/types" } Then every command just picks up those defaults automatically. Try it right now Run it directly with npx — no install needed: npx snaptype from-url https://api.github.com/users/torvalds --zod -o src/types/github-user.ts Or add it as a dev dependency if you want a fixed version and faster runs: npm install -D snaptype snaptype from-url https://api.github.com/users/torvalds --zod -o src/types/github-user.ts Full docs at snaptype.dev · Source on GitHub . If it saves you time, a ⭐ on the repo goes a long way — it helps other developers find the tool. Built this after one too many mornings spent re-typing interfaces from API docs. If from-url or from-openapi saves you time on a real project, drop a comment — I'm curious what APIs people are working with. Feedback welcome snaptype is still early-stage and I'm actively shaping what it becomes. If you hit a use case it doesn't handle, a command that behaves unexpectedly, or a feature you'd find genuinely useful — open an issue on GitHub or just leave a comment below. Every piece of feedback helps. You're integrating a third-party API. You open the docs, find a sample response, and start typing: interface Product { id : number ; name : string ; price : number ; // wait, is currency a string or an object? // what's the shape of variants[]? // does stock ever go null? } Twenty minutes later you have a fragile interface you're not fully confident in — and the moment the API ships a new field, it's already stale. There's a better way. snaptype: point it at the source, get types back snaptype is a CLI that generates TypeScript interfaces and Zod schemas by reading your actual data — live API endpoints, OpenAPI specs, JSON files, CSV files. No config. No code to write. One command. from-url — types straight from a live endpoint This is the one I use most. Give it any JSON endpoint: npx snaptype from-url https://api.example.com/products --zod -o src/types/product.ts snaptype fetches the response and generates: export interface Product { id : number ; name : string ; price : number ; currency : " USD " | " EUR " | " GBP " ; stock : number | null ; createdAt : string ; variants : Variant []; } export interface Variant { sku : string ; color : string ; size : " S " | " M " | " L " | " XL " ; } export const ProductSchema = z . object ({ id : z . number (), name : z . string (), price : z . number (), currency : z . enum ([ " USD " , " EUR " , " GBP " ]), stock : z . number (). nullable (), createdAt : z . iso . datetime (), variants : z . array ( VariantSchema ), }); A few things worth noticing: Nested objects are expanded — variants[] became its own Variant interface automatically Enums are inferred — currency and size had a small set of repeated values, so snaptype made them union types instead of string Nullables are detected — stock was null in some responses, so it became number | null Semantic types — createdAt became z.iso.datetime() , not a generic z.string() This is inference against real data, not a naive field-by-field mapping. Real-world example: GitHub API Point it at any real endpoint — here the GitHub user API: npx snaptype from-url https://api.github.com/users/torvalds --zod -o src/types/github-user.ts Done. You now have typed access to every field in that response — including nested objects — with Zod schemas you can use directly for runtime validation. When GitHub ships a new field, re-run the command. No manual diffing. from-openapi — one file per schema, from any spec If the API you're integrating has an OpenAPI or Swagger spec (most do), this is even better. Point it at the spec file: npx snaptype from-openapi ./openapi.yaml -o src/types/ Or directly at the hosted spec URL: npx snaptype from-openapi https://petstore3.swagger.io/api/v3/openapi.json -o src/types/ snaptype reads every schema definition in the spec and generates one typed file per schema. YAML and JSON both work. For a spec that defines Pet , Order , and User , you get three files: // src/types/Pet.ts export interface Pet { id ?: number ; name : string ; photoUrls : string []; status ?: " available " | " pending " | " sold " ; tags ?: Tag []; category ?: Category ; } // src/types/Order.ts export interface Order { id ?: number ; petId ?: number ; quantity ?: number ; shipDate ?: string ; status ?: " placed " | " approved " | " delivered " ; complete ?: boolean ; } Before, setting up a typed client from an OpenAPI spec meant: Finding (or writing) a code generator Configuring it Running it Cleaning up the generated mess Updating it every time the spec changes Now it's one command. Works with private and authenticated APIs One-off request — pass headers directly with -H : npx snaptype from-url https://api.example.com/me \ -H "Authorization: Bearer $TOKEN " \ --zod -o src/types/me.ts Multiple endpoints on the same API — store the headers in .snaptyperc so you don't repeat yourself: { "baseUrl" : "https://api.example.com" , "headers" : { "Authorization" : "Beare