javascript - TypescriptでJSON文字列を解析する方法
TypeScriptで文字列をJSONとして解析する方法はありますか?
例えば、JavaScriptではJSON.parse()を使用できます。TypeScriptにも同様の関数はありますか?
次のような JSON オブジェクトの文字列があります:
{"name": "Bob", "error": false}
回答 #1
TypeScriptはJavaScriptの(スーパーセット)なので、JavaScriptで行うのと同じようにJSON.parseを使います:
let obj = JSON.parse(jsonString);
ただし、TypeScriptでは、結果として得られるオブジェクトの型も持つことができます:
interface MyObj {
myString: string;
myNumber: number;
}
let obj: MyObj = JSON.parse('{ "myString": "string", "myNumber": 4 }');
console.log(obj.myString);
console.log(obj.myNumber);
(プレイグラウンドでのコード)
回答 #2
型安全なJSON.parse
TypeScriptはJavaScriptの上位互換であるため、JSON.parseを使い続けることができます。
これは、任意の動作するJavaScriptコードをTypeScriptファイルに入れて、正確にどのように書かれているかを気にすることなく使えることを意味します。
問題が残っています: JSON.parse は any を返します。これは型安全性を損ないます(any を使わないでください)。
より強力な型のための3つの解決策を、複雑さの低い順に示します:
1. ユーザー定義型ガード
遊び場
// For example, you expect to parse a given value with `MyType` shape
type MyType = { name: string; description: string; }
// Validate this value with a custom type guard (extend to your needs)
function isMyType(o: any): o is MyType {
return "name" in o && "description" in o
}
const json = '{ "name": "Foo", "description": "Bar" }';
const parsed = JSON.parse(json);
if (isMyType(parsed)) {
// do something with now correctly typed object
parsed.description
} else {
// error handling; invalid JSON format
}
isMyType は型ガードと呼ばれます。その利点は、真となる if ブランチ内で完全に型付けされたオブジェクトを取得できることです。
2. 汎用の JSON.parse ラッパー
遊び場
JSON.parse の周りに汎用のラッパーを作成します。これは 1 つの型ガードを入力として取り、解析された型付きの値またはエラー結果を返します。
const safeJsonParse = <T>(guard: (o: any) => o is T) =>
(text: string): ParseResult<T> => {
const parsed = JSON.parse(text)
return guard(parsed) ? { parsed, hasError: false } : { hasError: true }
}
type ParseResult<T> =
| { parsed: T; hasError: false; error?: undefined }
| { parsed?: undefined; hasError: true; error?: unknown }
使用例:
const json = '{ "name": "Foo", "description": "Bar" }';
const result = safeJsonParse(isMyType)(json) // result: ParseResult<MyType>
if (result.hasError) {
console.log("error :/") // further error handling here
} else {
console.log(result.parsed.description) // result.parsed now has type `MyType`
}
`safeJsonParse` は、迅速に失敗するように拡張したり、`JSON.parse` のエラーを try/catch で処理するように拡張することができます。
3. 外部ライブラリ
多くの異なる値を検証する必要がある場合、型ガード関数を手動で書くのは面倒になります。この作業を助けるライブラリがあります - 例(網羅的なリストではありません):
io-ts:fp-tsのピア依存関係があり、関数型プログラミングスタイルを使用zod:io-tsよりも手続き型/オブジェクト指向であることを目指しているtypescript-is:コンパイラAPI用のTSトランスフォーマーで、ttypescriptのような追加のラッパーが必要typescript-json-schema/ajv:型からJSONスキーマを作成し、ajvでそれを検証する
さらに詳しい情報
- 実行時型チェック #1573
- TypeScriptでのインターフェース型チェック
- TypeScript:外部データの検証
回答 #3
もしあなたがJSONに検証済みのTypeScript型を持たせたいのであれば、その検証作業は自分で行う必要があります。これは新しいことではありません。純粋なJavaScriptでも、同じことを行う必要があります。
検証
私は自分の検証ロジックを「変換」のセットとして表現するのが好きです。Descriptorを変換のマップとして定義します:
type Descriptor<T> = {
[P in keyof T]: (v: any) => T[P];
};
それから、これらの変換を任意の入力に適用する関数を作ることができます:
function pick<T>(v: any, d: Descriptor<T>): T {
const ret: any = {};
for (let key in d) {
try {
const val = d[key](v[key]);
if (typeof val !== "undefined") {
ret[key] = val;
}
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`could not pick ${key}: ${msg}`);
}
}
return ret;
}
今、私は自分のJSON入力を検証しているだけでなく、進めながらTypeScriptの型を構築しています。上記のジェネリック型は、結果があなたの「transforms」から型を推論することを保証します。
もし変換がエラーを投げる場合(これはバリデーションを実装する方法です)、どのキーがエラーを引き起こしたかを示す別のエラーでラップするのが好きです。
使用法
あなたの例では、これを次のように使います:
const value = pick(JSON.parse('{"name": "Bob", "error": false}'), {
name: String,
error: Boolean,
});
今、value は型が付けられます。なぜなら、String と Boolean はどちらも、入力を受け取り型付きの出力を返すという意味で「トランスフォーマー」だからです。
さらに、value は 実際に その型になります。言い換えれば、もし name が実際に 123 であった場合、それは有効な文字列を持つように "123" に変換されます。これは、実行時に任意の入力を受け取り string を返す組み込み関数である String を使用したためです。
ここでこれが動作しているのを見ることができます。自分で納得するために、次のことを試してみてください:
const valueの定義にカーソルを合わせると、ポップオーバーに正しい型が表示されるのがわかります。- サンプルを再実行する前に、
"Bob"を123に変更してみてください。コンソールでは、名前が正しく文字列"123"に変換されたことが確認できます。
