使用TypeScript装饰器扩展ES6类时扩展类型

问题描述 投票:3回答:3

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

这是我的示例装饰类:

@decorator
class Person{

}

这是装饰者:

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

myProp不是人的已知财产:

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

如何装饰typescript类并保留类型完成,类型安全等?

typescript ecmascript-6 es6-class typescript-decorator typescript-class
3个回答
1
投票

为了补充jcalz response,回到Decorator Pattern的定义,它不会改变其目标的接口/合约。这不仅仅是术语。 TypeScript装饰器与Java注释和.NET属性共享相似之处,这些属性与不更改接口的事实相对应:它们只是添加元数据。

class mixin是解决问题的好选择。但最好不要在其名称中使用“decorator”以避免混淆。


3
投票

有一个GitHub issue about this有很多讨论。我认为它的总结是:装饰者不会改变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

这有帮助吗?祝好运!


2
投票

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

假设您有一个具有一些属性和方法的类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和typescript语言服务将从这两个类中找到属性。它模仿了多重遗产,但它确实是一个级联。

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

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

另一个好处是你可以将mixin mixin1(mixin2 ....(Base))链接到同一个类中拥有尽可能多的属性。

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

© www.soinside.com 2019 - 2024. All rights reserved.