TypeScript:从 JSON 反序列化为可区分联合
TypeScript: Deserialize from JSON to Discriminated Union
给定 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()
,它的第二个参数需要一个"prototype"。使用 classes,例如Excel
,一个人就可以做到Excel.prototype
。但是使用上面的 Discriminated Union,我遇到了一个错误:
error TS2693: 'FileType' only refers to a type, but is being used as a value here.
问题:
- 有什么方法可以反序列化 TypeScript 中的可区分联合吗?
- 如果不是,是否还有其他 优雅 方式来实现上述场景(给定两个 classes:
Excel
/Csv
和 JSON 字符串序列化它们中的任何一个;取回正确的实例化对象),不管有什么技巧,classes,class 继承、接口、有区别的联合……?
环境
- 打字稿 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
很多),尤其是在添加新的 classes 时。此外,开发人员必须选择一个唯一字段来检查每个 class...
在您的示例中,FileType
不是 class,它只是一个编译时联合类型。没有为 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 { . . . }
是Excel returns 一个布尔值,但是通过以这种方式编写 return 类型,TypeScript 理解它正在读取被歧视联合的鉴别器。您可以通过任何方式检查,例如检查是否定义了 (<any>f).Sheet
。
给定 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()
,它的第二个参数需要一个"prototype"。使用 classes,例如Excel
,一个人就可以做到Excel.prototype
。但是使用上面的 Discriminated Union,我遇到了一个错误:
error TS2693: 'FileType' only refers to a type, but is being used as a value here.
问题:
- 有什么方法可以反序列化 TypeScript 中的可区分联合吗?
- 如果不是,是否还有其他 优雅 方式来实现上述场景(给定两个 classes:
Excel
/Csv
和 JSON 字符串序列化它们中的任何一个;取回正确的实例化对象),不管有什么技巧,classes,class 继承、接口、有区别的联合……?
环境
- 打字稿 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
很多),尤其是在添加新的 classes 时。此外,开发人员必须选择一个唯一字段来检查每个 class...
在您的示例中,FileType
不是 class,它只是一个编译时联合类型。没有为 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 { . . . }
是Excel returns 一个布尔值,但是通过以这种方式编写 return 类型,TypeScript 理解它正在读取被歧视联合的鉴别器。您可以通过任何方式检查,例如检查是否定义了 (<any>f).Sheet
。