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

Sam*_*mih 9 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

let mc = new MyClass({ propB: false });   //mc.propA == 'defaultValue', mc.propB === false
Run Code Online (Sandbox Code Playgroud)

我不喜欢这需要我多次复制属性名称。谁能建议一种干净的方法来实现这一目标?

Sha*_*ard 2

我没有看到将配置对象作为成员对象添加到类中的大问题。目前所有借助最新 Typescript 版本的解决方案都不是很好,而且只是让像这样简单的事情变得过于复杂。在将解构与参数属性结合之前,已经提出了类似的建议。这个提案已经有近两年的历史了。然而,有很多方法可以实现上述目的,我不确定的是您是否也需要访问类中的字段。

考虑到以下界面,一些可能帮助您解决上述问题的方法......

interface IConfigOpts {
    propA: string,
    propB: boolean
    propC: number
}
Run Code Online (Sandbox Code Playgroud)

...以及这个类结构。

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]}));
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以按照 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
Run Code Online (Sandbox Code Playgroud)

但这不允许您this.propA在类中使用类型检查。您(还)无法使用解构将类型化类成员动态绑定到类。并强迫你使用this[key]

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

prop<TKey extends keyof IConfigOpts, TValue extends IConfigOpts[TKey]>(key: TKey): TValue {
    return this[key as string];
}
Run Code Online (Sandbox Code Playgroud)

这可以确保输入的键存在于使用查找的界面中,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
Run Code Online (Sandbox Code Playgroud)

自动将(大量)属性绑定到类的唯一好处是,您不需要在给定构造函数中编辑对象的解构来匹配对象的键。例如propA更改为propAB. 您需要更改的只是接口声明。