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}
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}
JSON.parse
Anda mendapatkan objek sebagai hasil dan bukan string
(lihat jawaban saya untuk lebih lanjut). Jika Anda ingin mengubah objek menjadi string maka Anda perlu menggunakan JSON.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);
'{ "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 TypeScript
JSON.parse
Anda dapat terus menggunakan JSON.parse
, karena TS adalah superset JS. Masih ada masalah tersisa: JSON.parse
pengembalian any
, yang merusak keamanan tipe. Berikut dua opsi untuk tipe yang lebih kuat:
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:
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 }
Contoh penggunaan:
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 / menangkap JSON.parse
kesalahan.
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
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.
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.
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, karena String
dan Boolean
keduanya adalah "transformer" dalam arti mereka mengambil input dan mengembalikan output yang diketik.
Lebih jauh lagi, sebenarnyavalue
akan menjadi tipe itu. Dengan kata lain, jika name
sebenarnya 123
, itu akan diubah "123"
sehingga Anda memiliki string yang valid. Ini karena kami menggunakan String
pada waktu proses, fungsi bawaan yang menerima input arbitrer dan mengembalikan file string
.
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"
ke 123
dan kembali menjalankan sampel. Di konsol Anda, Anda akan melihat bahwa nama tersebut telah diubah dengan benar menjadi string "123"
.name
benar-benar 123
, itu akan diubah menjadi "123"
. Ini sepertinya tidak benar. Saya value
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 menggunakan Object
. 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.
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
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, }
);