C'è un modo per analizzare le stringhe come JSON in Typescript.
Esempio: in JS, possiamo usare JSON.parse(). Esiste una funzione simile in Typescript?
Ho una stringa oggetto JSON come segue:
{"name": "Bob", "error": false}
C'è un modo per analizzare le stringhe come JSON in Typescript.
Esempio: in JS, possiamo usare JSON.parse(). Esiste una funzione simile in Typescript?
Ho una stringa oggetto JSON come segue:
{"name": "Bob", "error": false}
JSON.parseottieni un oggetto come risultato e non un string(vedi la mia risposta per ulteriori informazioni). Se vuoi trasformare un oggetto in una stringa, devi JSON.stringifyinvece usare .
Risposte:
Il dattiloscritto è (un superset di) javascript, quindi lo usi JSON.parsecome faresti in javascript:
let obj = JSON.parse(jsonString);
Solo che in dattiloscritto puoi avere un tipo per l'oggetto risultante:
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 }'con '{ "myString": "string", "myNumberBAD": 4 }'non fallirà e obj.myNumber restituirà undefined.
Json.parse(text).validate[MyObj]. playframework.com/documentation/2.6.x/ScalaJson come puoi fare lo stesso in dattiloscritto (forse esiste una libreria esterna per farlo?)?
MyObjnon esiste. Ci sono molti altri thread in SO su questo argomento, ad esempio: Controlla se un oggetto implementa un'interfaccia in fase di esecuzione con TypeScript
JSON.parsePuoi continuare a utilizzare JSON.parse, poiché TS è un superset JS. C'è ancora un problema: i JSON.parseresiany , che mina la sicurezza dei tipi. Ecco due opzioni per i tipi più forti:
Le protezioni dei tipi personalizzate sono la soluzione più semplice e spesso sufficiente per la convalida dei dati esterni:
// 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
}
Un JSON.parsewrapper può quindi prendere una protezione del tipo come input e restituire il valore digitato analizzato:
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 }
Esempio di utilizzo:
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`
}
safeJsonParsepotrebbe essere esteso per fallire velocemente o try / catchJSON.parse errori.
Scrivere manualmente le funzioni di protezione dei tipi diventa complicato, se è necessario convalidare molti valori diversi. Ci sono biblioteche per aiutare con questa attività - esempi (nessun elenco completo):
io-ts: rel. popolare (3.2k stelle attualmente), fp-tsdipendenza tra pari, stile di programmazione funzionalezod: abbastanza nuovo (repo: 2020-03-07), si sforza di essere più procedurale / orientato agli oggetti rispetto aio-tstypescript-is: Trasformatore TS per API del compilatore, wrapper aggiuntivo come ttypescript necessariotypescript-json-schema/ ajv: Crea uno schema JSON dai tipi e convalidalo conajvSe desideri che il tuo JSON abbia un tipo di Typescript convalidato, dovrai eseguire tu stesso il lavoro di convalida. Questa non è una novità. In semplice Javascript, dovresti fare lo stesso.
Mi piace esprimere la mia logica di convalida come un insieme di "trasformazioni". Definisco una Descriptorcome mappa delle trasformazioni:
type Descriptor<T> = {
[P in keyof T]: (v: any) => T[P];
};
Quindi posso creare una funzione che applicherà queste trasformazioni a input arbitrari:
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;
}
Ora, non solo sto convalidando il mio input JSON, ma sto creando un tipo Typescript mentre procedo. I tipi generici di cui sopra assicurano che il risultato deduca i tipi dalle tue "trasformazioni".
Nel caso in cui la trasformazione generi un errore (che è il modo in cui implementeresti la convalida), mi piace includerlo con un altro errore che mostra quale chiave ha causato l'errore.
Nel tuo esempio, lo userei come segue:
const value = pick(JSON.parse('{"name": "Bob", "error": false}'), {
name: String,
error: Boolean,
});
Ora valueverrà digitato, poiché Stringe Booleansono entrambi "trasformatori" nel senso che prendono l'input e restituiscono un'uscita digitata.
Inoltre, valuesarà effettivamente quel tipo. In altre parole, se namefosse effettivamente 123, verrà trasformato in in "123"modo da avere una stringa valida. Questo perché abbiamo utilizzato Stringin fase di esecuzione, una funzione incorporata che accetta input arbitrari e restituisce un filestring .
Puoi vederlo funzionare qui . Prova le seguenti cose per convincerti:
const valuedefinizione per vedere che il pop-over mostra il tipo corretto."Bob"per 123e ri-eseguire l'esempio. Nella tua console, vedrai che il nome è stato correttamente convertito nella stringa "123".nameerano in realtà 123, si sarà trasformato a "123"Questo sembra essere errato mio.. valueè tornato {name: 123..non {name:"123"..quando copio incollare tutto il codice esattamente e fare che il cambiamento.
123posto di "Bob").
Transformedtipo. Puoi semplicemente usare Object. type Descriptor<T extends Object> = { ... };
Transformedtipo è totalmente inutile. Ho aggiornato la risposta di conseguenza.
Puoi inoltre usare librerie che eseguono la convalida del tipo del tuo json, come Sparkson . Ti consentono di definire una classe TypeScript, a cui vorresti analizzare la tua risposta, nel tuo caso potrebbe essere:
import { Field } from "sparkson";
class Response {
constructor(
@Field("name") public name: string,
@Field("error") public error: boolean
) {}
}
La libreria convaliderà se i campi obbligatori sono presenti nel payload JSON e se i loro tipi sono corretti. Può anche fare un sacco di convalide e conversioni.
C'è una grande libreria per esso ts-json-object
Nel tuo caso dovresti eseguire il codice seguente:
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});
Questa libreria convaliderà il json prima dell'analisi
JSON.parse è disponibile in TypeScript, quindi puoi semplicemente usarlo:
JSON.parse('{"name": "Bob", "error": false}') // Returns a value of type 'any'
Tuttavia, spesso vorrai analizzare un oggetto JSON assicurandoti che corrisponda a un certo tipo, piuttosto che trattare con un valore di tipo any. In tal caso, puoi definire una funzione come la seguente:
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;
}
Questa funzione accetta una stringa JSON e un oggetto contenente singole funzioni che caricano ogni campo dell'oggetto che stai creando. Puoi usarlo in questo modo:
const value = parse_json(
'{"name": "Bob", "error": false}',
{ name: String, error: Boolean, }
);