JSONをTypeScriptに変換
配列の全要素をマージしてインターフェースを生成。オプショナルが本当にオプショナルになります。
貼り付けたものがブラウザの外に出ることはありません。 connect-src の許可リストにより、これは約束ではなくブラウザによる保証になっています。 自分で確かめる
JSONのペイロードをTypeScriptのインターフェースに変換します。ネストしたオブジェクトはそれぞれ名前付きの型になり、配列はすべての要素をマージし、構造が同一の形は一度だけ出力して再利用します。
サンプルからの推論は当て推量です。このページの意義は、その推測が目に見えることにあります。
任意プロパティ。多くのジェネレーターが失敗するのはここ
配列の先頭要素だけを読んで終わるジェネレーターは、すべてのフィールドが必須のインターフェースを吐きます。任意フィールドを持たない最初のレコードで型チェックが落ち、結局生成された型を手で直すことになり、それでは意味がありません。
このツールはすべての要素をマージします。全要素にあるキーは必須、一部にしかないキーはクエスチョンマークを付けて任意と印を付けます。それはあなたのサンプルから取り出した本物の情報であり、単一要素だけを見るジェネレーターはそれを捨てています。
そのほかの判断
- nullはユニオンになる
- 文字列としてもnullとしても現れたフィールドはstring | nullです。anyでもstringでもありません。
- 空の配列はunknown[]
- any[]ではありません。空の配列は型の情報を持たず、any[]にすると、それに触れるものすべての型チェックを黙って無効にしてしまいます。
- 同じ形は重複排除される
- 500件のレコードのリストからは、500個ではなく1つのインターフェースができます。
- 配列要素の名前は単数形にする
- categoriesという配列からはCategoryというインターフェースができます。
- 識別子として不正なキーはクォートする
- has-dash、2fast、classといったキーは、クォート付きのプロパティ名になります。
- 安全でない整数には印を付ける
- サンプルに2^53-1を超える整数が入っていたのにnumberと型付けするのは嘘です。JavaScriptがその値を表現できないからです。注記でそのことを示します。正しい対処は上流にあり、文字列として送ることです。
生成された型が与えてくれないもの
TypeScriptのインターフェースは実行時に消えます。コンパイラに「何を期待しているか」を伝えるだけで、APIが違うものを返してきたときには何もしません。それには実行時バリデーターが必要であり、外部APIに対する誠実な進め方は、同じ定義から検証と型推論の両方を行うスキーマを使うことです。
Zod、Valibot、ArkTypeはいずれもそれができます。ペイロードを理解するためにここでインターフェースを生成し、自分の制御が及ばない境界には実行時スキーマを書いてください。
How to do this in code
同じ考え方をコードで書いた場合と、信頼できない境界で何を使うべきか。
sh quicktype
quicktypeは多くの出力言語に対応しています。重要なのは、複数のサンプルを渡すオプションです。
npx quicktype --lang ts --just-types --src-lang json payload.json
# Several samples, which is what makes optionality accurate
npx quicktype --lang ts --just-types samples/*.json ts Zod
APIの境界ではこのパターンを選んでください。インターフェースはコンパイラに伝え、スキーマは事実を告げます。
import { z } from 'zod';
const User = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
verifiedAt: z.string().nullable(),
roles: z.array(z.string()),
});
// One definition, both a runtime check and a static type
type User = z.infer<typeof User>;
const result = User.safeParse(await res.json());
if (!result.success) console.error(result.error.issues); ts リテラルからの型のみ
// If the data is a constant you control, TypeScript can infer
// the type without a generator at all.
const config = {
retries: 3,
endpoints: ['a', 'b'],
} as const;
type Config = typeof config; よくある質問
- APIは必ず送っているのに、なぜそのフィールドが任意になるのですか?
- サンプルの中の少なくとも1件にそのフィールドがなかったからです。本当に任意なのか、サンプルが不完全なのかのどちらかです。レコードをもっと含む配列を貼り付ければ、答えの精度が上がります。
- interfaceとtype、どちらを使うべきですか?
- オブジェクトの形を書くだけなら、ほとんど違いはありません。interfaceは宣言のマージができ、エラーメッセージがわずかに読みやすくなります。type別名はユニオンやマップ型を表現できます。上でどちらも選べます。
- これらの型は実行時に何かを検証しますか?
- しません。TypeScriptの型はコンパイル時に消えます。あなたが信じている内容を記述するだけで、それを確かめることはしません。ネットワーク境界を越えるデータには、実行時バリデーターを使ってください。