12

我正在尝试用装饰器(a-la-angular样式)装饰一个类,并向其添加方法和属性。

这是我的示例装饰类:

@decorator
class Person{

}

这是装饰器:

const decorator = (target)=>{
    return class New_Class extends target {
        myProp:string
    }
}

myProp不是 Person 的已知属性:

person.myProp //Error - myProp does not exist on type Person

如何装饰打字稿类并保留类型完成、类型安全等?

4

3 回答 3

6

我找到了一种实现多种遗产的解决方案(实际上是级联的),但值得一看。

假设您有一个 Base 类,其中包含一些属性和方法:

class Base {
    tableName = 'My table name';
    hello(name) {
     return `hello ${name}`;
    }
}

你想要一个类来扩展 Base 但你也定义了一些你想要重用的属性。为此将执行以下功能:

type Constructor<T = {}> = new (...args: any[]) => T;
function UserFields<TBase extends Constructor>(Base: TBase) {
    return class extends Base {
        name: string;
        email: string;
    };
}

现在我们可以做一个扩展 Base 和扩展 UserFields 的类,打字稿语言服务将从这两个类中找到属性。它模拟了多重遗产,但它实际上是一个级联。

class User extends UserFields(Base) { }
const u = new User();
u.tableName = 'users'; // ok
u.name = 'John'; // ok

通过这种方式,您可以将 UserFields 函数与任何其他类一起使用。一个明显的例子是,如果您想在客户端将 User 对象公开为“干净”对象并具有可用字段,然后您有一个 UserDb 对象与数据库连接,并且任何其他服务器端方法都可以具有相同的字段也。我们只定义一次数据库字段!

另一件好事是您可以链接 mixin mixin1(mixin2....(Base)) 以在同一个类中拥有尽可能多的属性。

每个人都希望装饰器属性可以被打字稿看到,但同时也是一个很好的解决方案。

于 2019-04-04T16:16:54.923 回答
5

为了补充jcalz 响应,回到装饰器模式的定义,它不会更改其目标的接口/合同。这不仅仅是术语。TypeScript 装饰器与 Java 注释和 .NET 属性有相似之处,这与不更改接口的事实相一致:它们只是添加元数据。

类 mixin 是解决您问题的好人选。但最好不要在名称中使用“装饰器”,以免混淆。

于 2019-02-27T10:52:21.127 回答
4

有一个关于这个的 GitHub 问题很多讨论。我认为它的总结是:装饰器不会改变a 的类型class(大部分讨论是关于它是否应该那样,因此你不能按照你想要的方式去做,就像这样:

const decorator = (target: new (...args: any) => any) => {
  // note I'm extending target, not Person, otherwise you're not
  // decorating the passed-in thing
  return class New_Class extends target {
    myProp!: string
  }
}

@decorator
class Person {
  noKnowledgeOfMyProp: this['myProp'] = "oops"; // error
}

declare const p: Person;
p.myProp; // error, uh oh

您可以做的只是将您的装饰器用作普通的mixin函数,并Person扩展它的返回值。你最终有两个类定义......一个是传入的decorator,一个是你的新类扩展的。“内部”类(传递给decorator())仍然不知道添加的道具,但“外部”类可以:

class Person extends decorator(class {
  innerProp: string = "inner";
  noKnowledgeOfMyProp: this['myProp'] = "oops"; // error
}) {
  outerProp: string = "outer"
  hasKnowledgeOrMyProp: this['myProp'] = "okay"; // okay
}

declare const p: Person;
p.myProp; // okay

这有帮助吗?祝你好运!

于 2019-02-26T19:13:29.500 回答