serialisering - TypeScript: Deserialiser fra JSON til diskriminert union
Gitt Typescript-kodesnutten:
class Excel {
Password: string;
Sheet: number;
}
class Csv {
Separator: string;
Encoding: string;
}
type FileType = Excel | Csv
let input = '{"Separator": ",", "Encoding": "UTF-8"}';
let output = Object.setPrototypeOf(JSON.parse(input), FileType.prototype) // error!
I TypeScript/Javascript, for å deserialisere fra JSON, kan man bruke Object.setPrototypeOf(), hvor den andre parameteren krever en "prototype". Med klasser, f.eks. Excel, kan man bare gjøre Excel.prototype. Men med Discriminated Union som ovenfor, støtte jeg på en feil:
error TS2693: 'FileType' only refers to a type, but is being used as a value here.
Spørsmål :
- Finnes det en måte å deserialisere en diskriminert union i TypeScript på?
- Hvis ikke, finnes det noen annen elegant måte å realisere scenariet ovenfor på (gitt to klasser:
Excel/Csvog JSON-streng som serialiserer en av dem; få tilbake korrekt instansiert objekt), uansett hvilke triks, klasser, klassearv, grensesnitt, diskriminert union eller ikke...?
Miljø
- Typescript v2.9.2
- Visual Studio Code v1.25.1
Mitt forsøk
let json = JSON.parse(input);
let output: FileType | null = null;
if (json["Separator"]) {
console.log("It's csv");
output = Object.setPrototypeOf(json, Csv.prototype)
} else if (json["Password"]) {
console.log("It's excel");
output = Object.setPrototypeOf(json, Excel.prototype)
} else {
console.log("Error");
}
Det er lett å gjenkjenne at denne tilnærmingen er tungvint (if else mye), spesielt når man legger til nye klasser. I tillegg må utviklere velge et unikt felt for sjekking i hver klasse...
Svar #1
I eksemplet ditt er FileType ikke en klasse, det er bare en samlingstype som eksisterer under kompilering. Det genereres ingen kjørbar kode for FileType, så selv om typekontrolleren forstår hva det betyr, finnes det ingen FileType-objekt definert under kjøring for å hente prototype-egenskapen fra.
Jeg forstår ikke helt hvorfor du må sette prototypen til det deserialiserte objektet i utgangspunktet. Hvorfor ikke bare erklære det på denne måten:
let output = JSON.parse(input) as FileType;
if (IsExcel(output)) { /* do stuff with output.Password & .Sheet */ }
else { /* do stuff with output.Seperator and .Encoding }
IsExcel() er for å avgjøre om det deserialiserte objektet er av Excel-typen, det bør skrives som en Brukerdefinert Typevakt, men det kan være noe som
function IsExcel(f: FileType): f is Excel { . . . }
IsExcel returnerer en boolsk verdi, men ved å skrive returtypen på denne måten forstår TypeScript at den leser diskriminatoren for den diskriminerte unionen. Du kan sjekke på hvilken som helst måte du vil, for eksempel ved å sjekke om (<any>f).Sheet er definert.
