Czy istnieje sposób na analizowanie ciągów jako JSON w Typescript.
Przykład: W JS możemy użyć JSON.parse()
. Czy w Typescript jest podobna funkcja?
Mam ciąg obiektu JSON w następujący sposób:
{"name": "Bob", "error": false}
javascript
json
string
typescript
ssd20072
źródło
źródło
JSON.parse
, otrzymujesz obiekt jako wynik, a niestring
(zobacz moją odpowiedź po więcej). Jeśli chcesz zamienić obiekt w ciąg, musisz użyćJSON.stringify
zamiast tego.Odpowiedzi:
Maszynopis to (nadzbiór) javascript, więc używasz go tak,
JSON.parse
jak w javascript:let obj = JSON.parse(jsonString);
Tyle tylko, że w maszynopisie możesz mieć typ do wynikowego obiektu:
interface MyObj { myString: string; myNumber: number; } let obj: MyObj = JSON.parse('{ "myString": "string", "myNumber": 4 }'); console.log(obj.myString); console.log(obj.myNumber);
( kod na placu zabaw )
źródło
'{ "myString": "string", "myNumber": 4 }'
przez'{ "myString": "string", "myNumberBAD": 4 }'
nie zakończy się niepowodzeniem, a obj.myNumber zwróci wartość undefined.Json.parse(text).validate[MyObj]
. playframework.com/documentation/2.6.x/ScalaJson jak możesz zrobić to samo w maszynie (może jest do tego zewnętrzna biblioteka?)?MyObj
nie istnieje. W SO jest wiele innych wątków na ten temat, na przykład: Sprawdź, czy obiekt implementuje interfejs w czasie wykonywania z TypeScriptBezpieczne dla typów
JSON.parse
Możesz nadal używać
JSON.parse
, ponieważ TS to nadzbiór JS. Nadal pozostaje problem:JSON.parse
zwracaany
, co podważa bezpieczeństwo typów. Oto dwie opcje dla silniejszych typów:1. Osłony typu zdefiniowanego przez użytkownika ( plac zabaw )
Zabezpieczenia typu niestandardowego są najprostszym rozwiązaniem i często wystarczają do weryfikacji danych zewnętrznych:
// 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 }
Opakowanie
JSON.parse
może następnie przyjąć typ ochrony jako dane wejściowe i zwrócić przeanalizowaną, wpisaną wartość:
Przykład użycia: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
może zostać przedłużony, aby szybko zawieść lub spróbować / złapaćJSON.parse
błędy.2. Biblioteki zewnętrzne
Ręczne pisanie funkcji ochrony typów staje się uciążliwe, jeśli trzeba zweryfikować wiele różnych wartości. Istnieją biblioteki, które mogą pomóc w tym zadaniu - przykłady (brak pełnej listy):
io-ts
: rel. popularny (obecnie 3,2 tys. gwiazdek),fp-ts
zależność od rówieśników, funkcjonalny styl programowaniazod
: całkiem nowy (repo: 2020-03-07), stara się być bardziej proceduralny / obiektowy niżio-ts
typescript-is
Transformator TS do kompilatora API dodatkowe owinięcie jak ttypescript potrzebnetypescript-json-schema
/ajv
: Utwórz schemat JSON z typów i zweryfikuj go za pomocąajv
Więcej informacji
źródło
Jeśli chcesz, aby twój JSON miał zweryfikowany typ skryptu, musisz sam wykonać tę walidację. To nic nowego. W zwykłym JavaScript musisz zrobić to samo.
Uprawomocnienie
Lubię wyrażać swoją logikę walidacji jako zbiór „transformacji”. Definiuję a
Descriptor
jako mapę przekształceń:type Descriptor<T> = { [P in keyof T]: (v: any) => T[P]; };
Następnie mogę utworzyć funkcję, która zastosuje te przekształcenia do dowolnego wejścia:
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; }
Teraz nie tylko sprawdzam poprawność danych wejściowych JSON, ale buduję typ Typescript na bieżąco. Powyższe typy ogólne zapewniają, że wynik wywnioskuje typy z Twoich „przekształceń”.
W przypadku, gdy transformacja zgłasza błąd (tak jak zaimplementowałbyś walidację), chciałbym owinąć go innym błędem pokazującym, który klucz spowodował błąd.
Stosowanie
W twoim przykładzie użyłbym tego w następujący sposób:
const value = pick(JSON.parse('{"name": "Bob", "error": false}'), { name: String, error: Boolean, });
Teraz
value
zostaną wpisane, ponieważString
iBoolean
to zarówno „Transformers” w sensie podejmowania wejście i powrót wpisywanych wyjście.Co więcej,
value
testament faktycznie będzie tego typu. Innymi słowy, jeśliname
faktycznie tak było123
, zostanie przekształcone w"123"
, aby uzyskać prawidłowy ciąg. Dzieje się tak, ponieważString
w czasie wykonywania używaliśmy wbudowanej funkcji, która akceptuje dowolne dane wejściowe i zwraca plikstring
.Możesz zobaczyć, jak to działa tutaj . Spróbuj następujących rzeczy, aby się przekonać:
const value
definicję, aby zobaczyć, że wyskakujące okienko pokazuje prawidłowy typ."Bob"
się123
i ponownie uruchomić próbki. W konsoli zobaczysz, że nazwa została poprawnie przekonwertowana na ciąg"123"
.źródło
name
faktycznie był123
, zostanie przekształcony na"123"
. Wydaje się, że jest to niepoprawne. Mójvalue
wraca{name: 123..
nie,{name:"123"..
kiedy123
zamiast"Bob"
).Transformed
typ. Możesz po prostu użyćObject
.type Descriptor<T extends Object> = { ... };
Transformed
Typ jest całkowicie zbędne. Odpowiednio zaktualizowałem odpowiedź.123
aby był on automatycznie konwertowany na ciąg"123"
, ponieważ jest to liczba w obiekcie JSON.Możesz dodatkowo użyć bibliotek, które wykonują sprawdzanie poprawności typu json, takich jak Sparkson . Pozwalają zdefiniować klasę TypeScript, do której chcesz przeanalizować swoją odpowiedź, w twoim przypadku może to być:
import { Field } from "sparkson"; class Response { constructor( @Field("name") public name: string, @Field("error") public error: boolean ) {} }
Biblioteka sprawdzi, czy wymagane pola znajdują się w ładunku JSON i czy ich typy są poprawne. Może również przeprowadzić szereg weryfikacji i konwersji.
źródło
Jest do tego świetna biblioteka ts-json-object
W twoim przypadku musisz uruchomić następujący kod:
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});
Ta biblioteka sprawdzi poprawność pliku json przed analizowaniem
źródło
JSON.parse
jest dostępny w języku TypeScript, więc możesz go po prostu użyć:JSON.parse('{"name": "Bob", "error": false}') // Returns a value of type 'any'
Jednak często będziesz chciał przeanalizować obiekt JSON, upewniając się, że pasuje do określonego typu, zamiast zajmować się wartością typu
any
. W takim przypadku możesz zdefiniować funkcję, taką jak: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; }
Ta funkcja przyjmuje ciąg JSON i obiekt zawierający indywidualne funkcje, które ładują każde pole tworzonego obiektu. Możesz go używać w ten sposób:
const value = parse_json( '{"name": "Bob", "error": false}', { name: String, error: Boolean, } );
źródło