Apakah ada cara untuk mengurai string sebagai JSON di Typecript.
Contoh: Di JS, kita bisa menggunakan JSON.parse()
. Apakah ada fungsi serupa di Ketikan?
Saya memiliki string objek JSON sebagai berikut:
{"name": "Bob", "error": false}
javascript
json
string
typescript
ssd20072.dll
sumber
sumber
JSON.parse
Anda mendapatkan objek sebagai hasil dan bukanstring
(lihat jawaban saya untuk lebih lanjut). Jika Anda ingin mengubah objek menjadi string maka Anda perlu menggunakanJSON.stringify
sebagai gantinya.Jawaban:
Ketikan adalah (superset dari) javascript, jadi Anda hanya menggunakan
JSON.parse
seperti yang Anda lakukan di javascript:let obj = JSON.parse(jsonString);
Hanya di skrip ketikan Anda bisa memiliki tipe ke objek yang dihasilkan:
interface MyObj { myString: string; myNumber: number; } let obj: MyObj = JSON.parse('{ "myString": "string", "myNumber": 4 }'); console.log(obj.myString); console.log(obj.myNumber);
( kode di taman bermain )
sumber
'{ "myString": "string", "myNumber": 4 }'
dengan'{ "myString": "string", "myNumberBAD": 4 }'
tidak akan gagal, dan obj.myNumber akan mengembalikan undefined.Json.parse(text).validate[MyObj]
. playframework.com/documentation/2.6.x/ScalaJson bagaimana Anda bisa melakukan hal yang sama dalam skrip ketikan (mungkin ada perpustakaan eksternal untuk melakukannya?)?MyObj
tidak ada. Ada banyak utas lain di SO tentang subjek ini, misalnya: Periksa apakah suatu objek mengimplementasikan antarmuka saat runtime dengan TypeScriptKetik aman
JSON.parse
Anda dapat terus menggunakan
JSON.parse
, karena TS adalah superset JS. Masih ada masalah tersisa:JSON.parse
pengembalianany
, yang merusak keamanan tipe. Berikut dua opsi untuk tipe yang lebih kuat:1. Pelindung tipe yang ditentukan pengguna ( taman bermain )
Pelindung tipe kustom adalah solusi paling sederhana dan seringkali cukup untuk validasi data eksternal:
// 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 function isMyType(o: any): o is MyType { return "name" in o && "description" in o }
Sebuah
JSON.parse
wrapper kemudian dapat mengambil penjaga tipe sebagai masukan dan kembali diurai, nilai diketik:
Contoh penggunaan: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
mungkin diperpanjang untuk gagal dengan cepat atau mencoba / menangkapJSON.parse
kesalahan.2. Perpustakaan eksternal
Menulis fungsi pelindung tipe secara manual menjadi rumit, jika Anda perlu memvalidasi banyak nilai yang berbeda. Ada perpustakaan untuk membantu tugas ini - contoh (tidak ada daftar lengkap):
io-ts
: rel. populer (saat ini 3,2k bintang),fp-ts
ketergantungan rekan, gaya pemrograman fungsionalzod
: cukup baru (repo: 2020-03-07), berusaha untuk lebih prosedural / berorientasi objek daripadaio-ts
typescript-is
: Transformator TS untuk compiler API, diperlukan pembungkus tambahan seperti ttypescripttypescript-json-schema
/ajv
: Buat skema JSON dari jenis dan validasi denganajv
Info selengkapnya
sumber
Jika Anda ingin JSON Anda memiliki tipe Typecript yang divalidasi, Anda perlu melakukan pekerjaan validasi itu sendiri. Ini bukanlah hal baru. Dalam Javascript biasa, Anda perlu melakukan hal yang sama.
Validasi
Saya suka mengekspresikan logika validasi saya sebagai satu set "transformasi". Saya mendefinisikan a
Descriptor
sebagai peta transformasi:type Descriptor<T> = { [P in keyof T]: (v: any) => T[P]; };
Kemudian saya dapat membuat fungsi yang akan menerapkan transformasi ini ke input arbitrer:
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; }
Sekarang, saya tidak hanya memvalidasi input JSON saya, tetapi saya juga membangun tipe Typecript saat saya pergi. Jenis umum di atas memastikan bahwa hasil menyimpulkan jenis dari "transformasi" Anda.
Jika transformasi memunculkan kesalahan (yang merupakan cara Anda menerapkan validasi), saya ingin membungkusnya dengan kesalahan lain yang menunjukkan kunci mana yang menyebabkan kesalahan.
Pemakaian
Dalam contoh Anda, saya akan menggunakan ini sebagai berikut:
const value = pick(JSON.parse('{"name": "Bob", "error": false}'), { name: String, error: Boolean, });
Sekarang
value
akan diketik, karenaString
danBoolean
keduanya adalah "transformer" dalam arti mereka mengambil input dan mengembalikan output yang diketik.Lebih jauh lagi, sebenarnya
value
akan menjadi tipe itu. Dengan kata lain, jikaname
sebenarnya123
, itu akan diubah"123"
sehingga Anda memiliki string yang valid. Ini karena kami menggunakanString
pada waktu proses, fungsi bawaan yang menerima input arbitrer dan mengembalikan filestring
.Anda dapat melihat ini berfungsi di sini . Cobalah hal-hal berikut untuk meyakinkan diri sendiri:
const value
definisi untuk melihat bahwa pop-over menampilkan jenis yang benar."Bob"
ke123
dan kembali menjalankan sampel. Di konsol Anda, Anda akan melihat bahwa nama tersebut telah diubah dengan benar menjadi string"123"
.sumber
name
benar-benar123
, itu akan diubah menjadi"123"
. Ini sepertinya tidak benar. Sayavalue
akan kembali{name: 123..
bukan{name:"123"..
ketika saya menyalin dan menempel semua kode Anda dengan tepat dan membuat perubahan itu.123
alih-alih"Bob"
).Transformed
tipe. Anda bisa menggunakanObject
.type Descriptor<T extends Object> = { ... };
Transformed
jenis adalah sama sekali tidak perlu. Saya telah memperbarui jawabannya.123
secara otomatis diubah menjadi string"123"
, karena itu adalah angka dalam objek JSON.Anda juga dapat menggunakan pustaka yang melakukan validasi tipe json Anda, seperti Sparkson . Mereka memungkinkan Anda untuk menentukan kelas TypeScript, yang ingin Anda parse responsnya, dalam kasus Anda bisa jadi:
import { Field } from "sparkson"; class Response { constructor( @Field("name") public name: string, @Field("error") public error: boolean ) {} }
Pustaka akan memvalidasi jika bidang wajib ada dalam payload JSON dan jika jenisnya benar. Itu juga dapat melakukan banyak validasi dan konversi.
sumber
Ada perpustakaan yang bagus untuk itu ts-json-object
Dalam kasus Anda, Anda perlu menjalankan kode berikut:
import {JSONObject, required} from 'ts-json-object' class Response extends JSONObject { @required name: string; @required error: boolean; } let resp = new Response({"name": "Bob", "error": false});
Library ini akan memvalidasi json sebelum melakukan parsing
sumber
JSON.parse
tersedia dalam TypeScript, jadi Anda bisa menggunakannya:JSON.parse('{"name": "Bob", "error": false}') // Returns a value of type 'any'
Namun, Anda sering kali ingin mengurai objek JSON sambil memastikannya cocok dengan tipe tertentu, daripada berurusan dengan nilai tipe
any
. Dalam hal ini, Anda dapat menentukan fungsi seperti berikut:function parse_json<TargetType extends Object>( json: string, type_definitions: { [Key in keyof TargetType]: (raw_value: any) => TargetType[Key] } ): TargetType { const raw = JSON.parse(json); const result: any = {}; for (const key in type_definitions) result[key] = type_definitions[key](raw[key]); return result; }
Fungsi ini mengambil string JSON dan objek yang berisi fungsi individual yang memuat setiap bidang objek yang Anda buat. Anda dapat menggunakannya seperti ini:
const value = parse_json( '{"name": "Bob", "error": false}', { name: String, error: Boolean, } );
sumber