首页 > 解决方案 > TypeScript:从 JSON 反序列化到有区别的联合

问题描述

给定 Typescript 代码片段:

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 字符串序列化它们中的任何一个;取回正确的实例化对象),不管任何技巧、类、类继承、接口、可区分联合或不...?

环境

我的尝试

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很多),尤其是在添加新类时。此外,开发人员必须选择一个独特的字段来检查每个类...

标签: typescriptserializationdiscriminated-union

解决方案


在您的示例中,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已定义。


推荐阅读