gpt4 book ai didi

typescript - Mixin 作为 TypeScript 中的类装饰器不会更新类属性

转载 作者:行者123 更新时间:2023-12-04 15:58:59 26 4
gpt4 key购买 nike

介绍

我在 TS 中有一个项目需要一些类来实现以下接口(interface):

interface IStylable {
readonly styles: {
[property: string]: string
};
addStyles (styles: { [property: string]: string }): void;
updateStyles (styles: { [property: string]: string }): void;
removeStyles (styles: Array<string>): void;
}

为了避免样板代码,我决定创建一个 Mixin并将其应用于我需要的每个类(class)。 (我可以使用抽象类,但我的问题需要多重继承解决方案,TS 不提供。) 下面是 IStylable 接口(interface)的类实现:

export class StylableClass implements IStylable {
private readonly _styles: { [property: string]: string } = {};

// For each property provided in styles param, check if the property
// is not already present in this._styles and add it. This way we
// do not overide existing property values.
public addStyles (styles: { [property: string]: string }): void {
for (const [property, value] of Object.entries(styles)) {
if (!this._styles.hasOwnProperty(property)) {
this._styles[property] = value;
}
}
}

// For each property provided in styles param, check if the property
// is already present in this._styles and add it. This way we
// do add property values values that do not exist.
public updateStyles (styles: { [property: string]: string }): void {
for (const [property, value] of Object.entries(styles)) {
if (this._styles.hasOwnProperty(property)) {
this._styles[property] = value;
}
}
}

// For each property in styles param, check if it is present in this._styles
// and remove it.
public removeStyles (styles: Array<string>): void {
for (const property of styles) {
if (this._styles.hasOwnProperty(property)) {
delete this._styles[property];
}
}
}

public set styles (styles: { [property: string]: string }) {
this.addStyles(styles);
}

public get styles (): { [property: string]: string } {
return this._styles;
}
}

让我真正兴奋和期待的是 ES6 中装饰器规范的标准化。 Typescript 通过在 tsconfig.json 中设置 experimentalDecorators 标志来允许此实验性功能。我希望将 StylableClass 用作类装饰器 (@Stylable) 以使代码更简洁,因此我创建了一个接受类并将其转换为装饰器的函数:

export function makeDecorator (decorator: Function) {
return function (decorated: Function) {
const fieldCollector: { [key: string]: string } = {};
decorator.apply(fieldCollector);
Object.getOwnPropertyNames(fieldCollector).forEach((name) => {
decorated.prototype[name] = fieldCollector[name];
});

Object.getOwnPropertyNames(decorator.prototype).forEach((name) => {
decorated.prototype[name] = decorator.prototype[name];
});
};
}

并像这样使用它:

export const Stylable = () => makeDecorator(StylableClass);

问题

现在是单元测试的时候了。我创建了一个虚拟类来应用我的装饰器,并为 addStyles() 方法编写了一个简单的测试。

@Stylable()
class StylableTest {
// Stylable
public addStyles!: (styles: {
[prop: string]: string;
}) => void;

public updateStyles!: (styles: {
[prop: string]: string;
}) => void;

public removeStyles!: (styles: string[]) => void;

public styles: { [property: string]: string } = {};
}

describe('Test Stylable mixin', () => {
it('should add styles', () => {
const styles1 = {
float: 'left',
color: '#000'
};

const styles2 = {
background: '#fff',
width: '100px'
};

// 1
const styles = new StylableTest();
expect(styles.styles).to.be.an('object').that.is.empty;

// 2
styles.addStyles(styles1);
expect(styles.styles).to.eql(styles1);

// 3
styles.addStyles(styles2);
expect(styles.styles).to.eql(Object.assign({}, styles1, styles2));
});
});

问题是第二个 expect 语句失败了。在执行 styles.addStyles(styles1); 之后,styles.styles 数组应该包含 styles1 对象时仍然是空的。当我调试我的代码时,我发现 addStyles() 方法中的 push 语句按预期执行,因此循环没有问题,但是数组在执行后没有更新方法的执行结束。您能否就我遗漏的内容提供提示或解决方案?我检查的第一件事是 makeDecorator 函数可能出了问题,但只要我可以执行这些方法,我就找不到其他线索来寻找。

最佳答案

StylableClass mixin 声明了一个名为styles 的属性。但是 StylableTest 创建了一个名为 styles 的字段,并为其分配了一个无人会使用的空对象。您需要将属性描述从装饰器转移到目标类,并从 StylableTest 中的 styles 中删除 = {}:

function makeDecorator(decorator) {
return function (decorated) {
var fieldCollector = {};
decorator.apply(fieldCollector);
Object.getOwnPropertyNames(fieldCollector).forEach(function (name) {
decorated.prototype[name] = fieldCollector[name];
});
Object.getOwnPropertyNames(decorator.prototype).forEach(function (name) {
var descriptor = Object.getOwnPropertyDescriptor(decorator.prototype, name);
if (descriptor) {
Object.defineProperty(decorated.prototype, name, descriptor);
}
else {
decorated.prototype[name] = decorator.prototype[name];
}
});
};
}

我可以建议 less error prone approach在 typescript 中混合。这种必须重新声明所有 mixin 成员的方法将在以后导致错误。至少避免使用类型查询重述字段的类型:

@Stylable()
class StylableTest {
// Stylable
public addStyles!: IStylable['addStyles']

public updateStyles!: IStylable['updateStyles']

public removeStyles!: IStylable['removeStyles']

public styles!: IStylable['styles']
}

关于typescript - Mixin 作为 TypeScript 中的类装饰器不会更新类属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50986378/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com