如何使用装饰器扩展类类型

时间:2021-04-12 11:04:00

标签: javascript typescript oop decorator

我想在我的项目中使用装饰器。这是我写的:

export const DetachedWindow = (options: TDetachedWindowOptions = defaultOptions) => <R extends Constructable>(Clazz: R): R => {
    const tmp = class extends Clazz {
        value = 'value';

        value2 = 'value2';

        constructor(...args: any[]) {
            super(...args);
            // <some logick>
        }
    };
    Object.defineProperty(tmp, 'name', { value: Clazz.name });
    return tmp;
};

正如你所见,这个装饰器创建了一些字段。但是打字稿无法识别它们

@DetachedWindow({ optA: 'optA' })
export class SomeClass {
    constructor() {
        setTimeout(() => {
            console.log(this.value); // TS2339: Property 'value' does not exist on type 'SomeClass'.
        });
    }
}

它确实存在。如果我在使用这些参数之前添加 @ts-ignore,代码可以正常工作。

我更糟,我怎样才能创建一个类装饰器,它会扩展父类的类型。我试图做这样的事情:

export const DetachedWindow = (options: TDetachedWindowOptions = defaultOptions) => <R extends Constructable>(Clazz: R): R & { value: string } => {

但这没有帮助。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

答案在 Class decorators 的 TypeScript 文档中:

// Note that the decorator _does not_ change the TypeScript type
// and so the new property `reportingURL` is not known
// to the type system:
bug.reportingURL;
Property 'reportingURL' does not exist on type 'BugReport'.

有点相关:Angular 使用装饰器来注释每个类,比如 @Component,它向类添加生命周期钩子,但作为开发人员,我们仍然需要实现一个接口来正确检索生命周期钩子签名。

Angular 的团队就是这样解决的。

interface OnInit {
  ngOnInit(): void
}

@Component({
  selector:    'app-hero-list',
  templateUrl: './hero-list.component.html',
})
export class HeroListComponent implements OnInit {
  ngOnInit(): void {
    // Initialization code
  }
}