Given the Typescript code snippet:
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!
In TypeScript/Javascript, to deserialize from JSON, one can use Object.setPrototypeOf()
, the second parameter of which requires a "prototype". With classes, e.g. Excel
, one can just do Excel.prototype
. But with Discriminated Union as above, I encountered an error:
error TS2693: 'FileType' only refers to a type, but is being used as a value here.
Excel
/Csv
and JSON string serializing either of them; get back the correct instantiated object), regardless of whatever tricks, classes, class inheritance, interface, discriminated union or not...?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");
}
It's easy to recognize that this approach is cumbersome (if else
alot), especially when adding new classes. Additionally, developers have to choose a unique field for checking in each class...
In your example, FileType
is not a class, it is only a compile-time union type. No runtime code is generated for FileType
, so while the type checker understands what it means, there is no FileType
object defined at runtime to retrieve the protoptype
property from.
I'm not clear why you need to set the prototype of the deserialized object in the first place. Why not just declare it this way:
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() is to determine if the deserialized object is of the Excel type, it should be written as a User-Defined Type Guard, but it might be something like
function IsExcel(f: FileType): f is Excel { . . . }
IsExcel returns a boolean, but by writing the return type this way TypeScript understands it is reading the discriminator for the discriminated union. You can check any way you want, for example by checking if (<any>f).Sheet
is defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With