2017-08-15 28 views
0

我目前正在将遗留代码转换为项目的打字稿。该项目包含许多用纯JavaScript编写的自定义类,它们遵循相同的构造函数,该构造函数接受可以在其中定义多个配置选项的配置对象。使用对象解构来分配类成员

我一直在玩弄如何在打字稿中定义允许的配置属性,还没有拿出一个我完全满意的解决方案。我首先声明了一个配置接口,它允许我定义可用的配置选项,但不能默认它们 - 我必须复制我的配置接口两次或更多的定义和初始化。我(想我)想是使用对象destrcuturing与默认值一旦定义类的成员,而不是具有如后担心:

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 

我不喜欢这需要我多次复制属性名称。任何人都可以提出一个很好的干净方式来实现这一目标?

+0

为什么不只是保存的配置对象,而不是它突破到属性? – gilamran

+0

@gilamran我的主要动机是我不希望配置选项可以在课堂以外进行修改。如果我参考配置对象,则创建者可以通过他们自己对配置对象的引用来修改配置选项,例如'let config = {myProp:'something'};新的MyClass(config); config.myProp ='somethingElse';'我不想要这个。 – Samih

回答

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]})); 
    } 
} 

您可以使用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进行类型检查。你不能()动态绑定类型成员到一个类使用破坏。并强制您改为使用this[key]

如果你不想在类的外部访问特性,你可以定义叫他们的名字属性,并使用它们的类型来验证定义的类型在给定的接口这省去了使用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更改为propAB。所有你必须改变的是接口声明。

1

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

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

请注意,这将使参考类型的浅拷贝。另请注意,如果MyClass的任何属性名称发生更改,则必须记住更改MyClassConfig中的属性名称,反之亦然。

+0

谢谢 - 我曾在一个ECMAScript 6问题中看到过这个解决方案,但想知道是否有打印机解决方案。我也必须支持IE11,所以希望避免使用polyfill。 – Samih

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章“分配给新的变量名,提供默认值”。

见JSBin了working sample