通过 TypeScript 中的装饰器向类添加属性

For*_*eti 13 decorator mixins typescript

在 TypeScript 的装饰器参考页面上,有一段代码被截断,说明如何使用类装饰器覆盖构造函数:

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

@classDecorator
class Greeter {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}

console.log(new Greeter("world"));
Run Code Online (Sandbox Code Playgroud)

并在日志中:

class_1 {
  property: 'property',
  hello: 'override',
  newProperty: 'new property' }
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。但是尝试newProperty通过点符号访问失败:

类型“Greeter”.ts(2339) 上不存在属性“newProperty”

错误并且未在 VS Code 的提示中列出。可以通过括号表示法访问它,但 TS 警告说

元素隐式具有 'any' 类型,因为类型 'Greeter' 没有索引签名。 ts(7017)

我错过了什么吗?如何以类型安全的方式通过装饰器实现添加新属性?我想像普通的类成员一样拥有普通的编译器支持。

Tit*_*mir 16

设计的装饰器不能改变类的类型。这仍在讨论中,并且在装饰者提案最终确定之前,团队似乎不会改变行为。你可以使用 mixins 来完成这个任务(阅读ts 中的 mixins

使用 mixins 代码看起来像:

function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

const Greeter = classDecorator(class {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
});
type Greeter = InstanceType<typeof Greeter> // have the instance type just as if we were to declare a class

console.log(new Greeter("world").newProperty);
Run Code Online (Sandbox Code Playgroud)


bla*_*ool 8

function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}
interface classInterface {
    newProperty: string;
    hello: string;
}

//trick
interface Greeter extends classInterface { };

@classDecorator
class Greeter {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}
const b = new Greeter();
console.log(b.newProperty);
Run Code Online (Sandbox Code Playgroud)

看来我们可以使用接口技巧来解决这个问题。技巧参考: /sf/answers/3666137611/

  • 这破坏了我们首先使用装饰器的原因。使用装饰器的目的是不必扩展类。如果没有,我们可以扩展一个实现这些新属性的类,并在 Greter 的构造函数中调用“super()”。 (7认同)