使用对象解构来分配类成员

时间:2017-08-15 10:02:32

标签: typescript

我目前正在将遗留代码转换为项目的打字稿。该项目包含许多用纯javascript编写的自定义类,它们遵循相同的模式,即具有一个构造函数,该构造函数接收一个配置对象,该配置对象可以在其中定义许多配置选项。

我一直在玩弄如何在打字稿中定义允许的配置属性,还没有找到一个我非常满意的解决方案。我开始声明配置的接口,这允许我定义可用的配置选项,但没有帮助默认它们 - 我必须复制我的配置接口两次或更多的定义和初始化。我(我想)想要的是使用对象描述来使用默认值定义类成员一次,之后不必担心它,例如:

export interface MyClassConfig {
    propA: string;
    propB: boolean;
}

export class MyClass {
    private propA: string = 'defautValue';
    private propB: boolean;

    constructor (config: MyClassConfig) {
        { propA, propB } = config;
    }
}

然后可以用以下内容初始化:

let mc = new MyClass({ propB: false });   //mc.propA == 'defaultValue', mc.propB === false

我不喜欢这需要我多次复制属性名称。有人能建议一个很好的清洁方法来实现这个目标吗?

3 个答案:

答案 0 :(得分:1)

我认为您不能使用解构来引用构造函数中this的属性。如何使用Object.assign将配置中的属性复制到实例?

constructor (config: MyClassConfig) {
    Object.assign(this, config);
}

请注意,这将生成引用类型的浅表副本。另请注意,如果MyClass的任何属性名称发生更改,则您必须记住更改MyClassConfig中的属性名称,反之亦然。

答案 1 :(得分:1)

我没有看到将配置对象作为成员对象添加到您的类的重大问题。在最新的Typescript版本的帮助下,所有当前的解决方案都不是很好,只是简单地复杂化了这个简单的东西。在Combining destructuring with parameter properties之前已经提出类似的建议。这个建议已有近2年的历史。但是,有很多方法可以做到这一点,我不确定的是你是否还需要访问你班级中的字段。

在给定以下界面的某些方法上可以帮助您解决上述问题......

interface IConfigOpts {
    propA: string,
    propB: boolean
    propC: number
}

......和这个班级结构。

class Config {

    constructor(opts: IConfigOpts) {
        // Object.assign(this, opts);
        Object.entries(opts).forEach((property: [string, any]) => {
            Object.defineProperty(this, property[0], {
                writable: true,
                value: property[1]
            });
        });
    }

    get props() {
        return Object.getOwnPropertyNames(this).map(key => ({[key]: this[key]}));
    }
}

您可以使用Frank建议的Object.assign(es5 +)或Object.defineProperty来初始化构造函数中的成员字段。后者提供了制作属性writable或指定getters和/或setters的选项。

您可以创建一个工厂方法,使用创建配置类和接口的新实例来获取类外部属性的属性:

const ConfigFactory = (opts: IConfigOpts) => new Config(opts) as Config & IConfigOpts;

const cfg = ConfigFactory({
    propA: 'propA',
    propB: true,
    propC: 5
});

let a: string = cfg.propA; // OK => defined

但这不允许您在类中使用this.propA进行类型检查。你不能()使用destructures动态地将类型化的类成员绑定到类。并强迫您使用this[key]

如果您不想访问类外部的属性,可以定义一个方法来按名称调用属性,并使用它们的类型来验证给定接口中定义的类型,这省略了{{1并返回给定的类型:

this[key]

这样可以确保输入的密钥存在于界面中,使用prop<TKey extends keyof IConfigOpts, TValue extends IConfigOpts[TKey]>(key: TKey): TValue { return this[key as string]; } 查找并返回正确的类型。

keyof

自动将(很多)属性绑定到类的唯一好处是,您不需要编辑给定构造函数中对象的析构以匹配对象的键。例如let a: string = cfg.prop('propA'); // OK => defined let b: boolean = cfg.prop('propB'); // OK => defined let c: number = cfg.prop('propC'); // OK => defined let d: number = cfg.prop('propD'); // Fail => Argument is not assignable to keyof IConfigOpts let e: boolean = cfg.prop('propC'); // Fail => Type number is not assignle to type boolean 更改为propA。所有你需要改变的是接口声明。

答案 2 :(得分:0)

我遇到了同样的问题:解构和分配给班级成员。

我找到了满足我需求的以下解决方案,也许是你的:

interface MyClassConfig {
    propA?: string; // Note the '?' for optional property
    propB: boolean;
}

class MyClass {
    private propA: string = 'defautValue';
    private propB: boolean;

    constructor (config: MyClassConfig) {
        ({ propA: this.propA = this.propA, propB: this.propB } = config);
    }
}

const myClass = new MyClass({ propA: 'aaa', propB: true });
console.log(myClass)

// MyClass:
//   propA = 'aaa';
//   propB = true;

const myClass2 = new MyClass({ propB: false });
console.log(myClass2)

// MyClass:
//   propA = 'defaultValue';
//   propB = false;

参见MDN Destructuring assignment章&#34;分配新变量名称并提供默认值&#34;。

请参阅JSBin了解working sample