我目前正在将遗留代码转换为项目的打字稿。该项目包含许多用纯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
我不喜欢这需要我多次复制属性名称。有人能建议一个很好的清洁方法来实现这个目标吗?
答案 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