【问题标题】:Possible to have TypeScript class optional properties fall back to default value if omitted?如果省略,可以让 TypeScript 类的可选属性回退到默认值吗?
【发布时间】:2017-04-08 15:11:37
【问题描述】:

如果用户省略了类成员属性的默认值,有什么方法可以分配它们?

我有一个自定义类,其中包含另一个类的可选实例:

export class MyClass1 {
    constructor() {
        this.name = "";
        this.classInstance = new MyClass2();
    }
    name: string;           // name of the object
    classInstance?: MyClass2;
}

class MyClass2 {
    constructor() {
        this.name = "MyClass2 initial name";
    }

    name: string;
}

MyClass1 的内容来自 JSON,所以有些时候成员变量 classInstance 可以是未定义的。在这些情况下,我希望 MyClass1 构造函数将 new MyClass2() 初始化到 classInstance 成员变量中。

这就是我分配创建 MyClass1 实例的方式:

let myNewClass: MyClass1 = {
    name: "MyClass"
}

因为 classInstance 被省略,我假设它会在构造函数中设置默认值。然而,这种情况并非如此。我还尝试从 classInstance 变量中删除可选符号,但这会产生编译错误,因为对象必须与 MyClass1 的结构相匹配。我想我通过强制转换成功编译没有错误,但它并没有解决问题(虽然我不确定这是否正确编译):

let myNewClass: MyClass1 = {
    name: "MyClass"
} as MyClass1

在这两种情况下,classInstance 都没有得到构造函数设置的默认值,结果是 null/undefined。

对我来说,如何解决转换问题并不重要(无论是设置属性可选还是使用转换),只要我可以控制默认值。

【问题讨论】:

    标签: typescript default


    【解决方案1】:

    这里的问题是

    let myNewClass: MyClass1 = {
        name: "MyClass"
    } as MyClass1
    

    这里是不是 MyClass1 实例。 TypeScript 类型系统被“欺骗”认为普通对象是 MyClass1 实例。

    它将被转译为var myNewClass = { name: "MyClass" }MyClass1 类未实例化。故事结束。

    实现预期的正确方法是将 JSON 中的值提供给类构造函数。考虑到对象属性(classInstance)可以是undefined,类属性不能依赖constructor({ classInstance = new MyClass2 }) { ... }等默认值,应该手动分配:

    export class MyClass1 {
        constructor(jsonMyClass1: MyClass1 = {}) {
            const { name, classInstance } = jsonMyClass1;
            this.name = 'name' in jsonMyClass1 ? name : "";
            this.classInstance = 'classInstance' in jsonMyClass1 ? classInstance : new MyClass2;
        }
        name: string;
        classInstance?: MyClass2;
    }
    
    let myNewClass: MyClass1 = new MyClass1({
        name: "MyClass"
    });
    

    当提供和处理数据时,考虑到类将如何使用它(即专门为未定义的值使用null),可以修改配方以使用语言提供的功能:

    export class MyClass1 {
      constructor(jsonMyClass1: MyClass1 = {} as MyClass1) {
            const { name = "", classInstance = new MyClass2 } = jsonMyClass1;
            this.name = name;
            this.classInstance = classInstance;
        }
        name: string;
        classInstance: MyClass2;
    }
    
    let myNewClass: MyClass1 = new MyClass1({
        name: "MyClass",
        classInstance: null
    });
    

    【讨论】:

    • 如此详尽的回答,谢谢!我只是想问一下这个:const { title = "", classInstance = new MyClass2 } = jsonMyClass1; 这个语法是做什么的?这是否意味着第一个 title 和 classInstance 被分配了值,然后 jsonMyClass1 中的字段覆盖了可用的值?但是如果创建了一个对象,你如何通过在下一行输入this.title = title; 来访问这些变量?我对语法有点困惑,我以前从未见过。你声明 const 变量,这是我理解的,但我不明白它是一个对象还是单独的变量?
    • 不客气。这是destructuring。它创建titleclassInstance 临时变量,这是为变量提供默认值的最简洁方式(如果jsonMyClass1 对象上不存在道具或者是undefined)。另一种方法是写类似this.classInstance = jsonMyClass1.classInstance !== undefined ? jsonMyClass1.classInstance : new Class2 的东西。您可以随时在typescriptlang.org/play 上查看它转译的内容,这是学习 TS 的好方法。
    • 谢谢!嗯,我又遇到了一个关于构造函数的问题:如果 classInstance 是 MyClass1 怎么办?我如何通过确保数据也通过嵌套子类的构造函数传递?例如,通过的 JSON 包含:{ name: "MyClass1 instance at main level", classInstance: { title: "MyClass1 instance nested" } } 如何确保 MyClass1 嵌套也使用构造函数创建?我从 json 接收到整个对象,所以在这种情况下我可以控制它的创建方式吗?
    • 可能应该类似于const { title, classInstance = new MyClass1 } = jsonMyClass1this.classInstance = classInstance instanceof MyClass1 ? classInstance : new MyClass1(classInstance)。这将导致递归类构造。如果您对这种方法有疑问,我建议您提出一个反映所有相关细节的新问题。请注意,即使对象来自解析 JSON,如果需要,您也可以预处理对象以更好地适应 MyClass1 构造函数。但是对于 classInstance,它应该按原样工作。
    • 再次感谢您。 :) 我最终为 MyClass1 下可能存在的所有子类编写了构造函数,并且 - 正如您所建议的那样 - MyClass1 构造函数将在其自己的构造函数(MyClass1)中调用它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 2021-09-16
    • 2018-01-03
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    • 2014-07-05
    相关资源
    最近更新 更多