TypeScript:从JSON反序列化为歧视联盟

时间:2018-07-27 23:27:44

标签: typescript serialization discriminated-union

给出打字稿代码段:

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!

在TypeScript / Javascript中,要从JSON反序列化,可以使用Object.setPrototypeOf(),其第二个参数需要“原型”。带班,例如Excel,一个人只能做Excel.prototype。但是使用上面的歧视联盟,我遇到了一个错误:

error TS2693: 'FileType' only refers to a type, but is being used as a value here.

问题:

  1. 有没有办法在TypeScript中反序列化一个有区别的联合?
  2. 如果没有,是否有其他优雅方式来实现上述方案(给定两个类:Excel / Csv和JSON字符串,将它们中的任何一个序列化;返回正确的实例化对象),无论是否有任何技巧,类,类继承,接口,有区别的联合...?

环境

  • 打字稿v2.9.2
  • Visual Studio代码v1.25.1

我的尝试

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");
}

很容易认识到这种方法很麻烦(很多if else),特别是在添加新类时。此外,开发人员必须选择一个唯一的字段来检入每个类...

1 个答案:

答案 0 :(得分:0)

在您的示例中,FileType不是类,它只是编译时联合类型。 FileType不会生成任何运行时代码,因此虽然类型检查器理解了它的含义,但在运行时没有定义FileType对象来从中检索protoptype属性。

我不清楚您为什么首先需要设置反序列化对象的原型。为什么不这样声明呢?

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()用于确定反序列化对象是否为Excel类型,应将其写为User-Defined Type Guard,但可能类似于

function IsExcel(f: FileType): f is Excel { . . . } 

IsExcel返回一个布尔值,但是通过以这种方式编写返回类型,TypeScript理解它正在读取已区分联合的区分符。您可以通过任何方式进行检查,例如,检查是否定义了(<any>f).Sheet