使用 TypeScript 装饰器扩展 ES6 class 时扩展类型
Extending type when extending an ES6 class with a TypeScript decorator
我正在尝试用装饰器(a-la-angular 样式)装饰 class,并向其添加方法和属性。
这是我装饰的例子class:
@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
如何装饰打字稿 class 并保留类型完成、类型安全等?
GitHub issue about this 有 很多 的讨论。我认为它的总结是:decorators do not mutate the type of a class
(大多数讨论是关于它是否should 或 不应该 那样),因此你不能按照你想要的方式去做,像这样:
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
扩展它的 return 值。你最终有两个 class 定义......一个被传递到 decorator
和一个你的新 class 扩展。 "inner" class(传递给 decorator()
)仍然不知道添加的道具,但 "outer" class 知道:
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
有帮助吗?祝你好运!
为了补充, going back to the definition of the Decorator Pattern,它不会改变其目标的interface/contracts。这不仅仅是术语。 TypeScript 装饰器与 Java 注释和 .NET 属性有相似之处,它们符合不更改接口的事实:它们只是添加元数据。
Class mixin 很适合解决您的问题。但最好不要在名称中使用"decorator",以免混淆。
我找到了一种实现某种多重继承的解决方案(真正将其级联),但值得一看。
假设您有一个具有一些属性和方法的 class 基础:
class Base {
tableName = 'My table name';
hello(name) {
return `hello ${name}`;
}
}
并且您想要 class 来扩展 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,打字稿语言服务将从这两个 class 中找到属性。它模拟了多重继承,但它实际上是一个级联。
class User extends UserFields(Base) { }
const u = new User();
u.tableName = 'users'; // ok
u.name = 'John'; // ok
通过这种方式,您可以将 UserFields 函数与任何其他 classes 重用。
一个明显的例子是,如果您想在客户端将 User 对象公开为 "clean" 对象并使字段可用,然后您有一个与数据库连接的 UserDb 对象,并且任何其他服务器端方法都可以具有同样的领域。我们只定义一次数据库字段!
另一个好处是你可以将 mixins mixin1(mixin2....(Base)) 链接起来,在同一个 class.
中拥有你想要的任意多的属性
每个人都希望装饰器属性在 typescript 中可见,但同时也是一个很好的解决方案。
我正在尝试用装饰器(a-la-angular 样式)装饰 class,并向其添加方法和属性。
这是我装饰的例子class:
@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
如何装饰打字稿 class 并保留类型完成、类型安全等?
GitHub issue about this 有 很多 的讨论。我认为它的总结是:decorators do not mutate the type of a class
(大多数讨论是关于它是否should 或 不应该 那样),因此你不能按照你想要的方式去做,像这样:
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
扩展它的 return 值。你最终有两个 class 定义......一个被传递到 decorator
和一个你的新 class 扩展。 "inner" class(传递给 decorator()
)仍然不知道添加的道具,但 "outer" class 知道:
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
有帮助吗?祝你好运!
为了补充
Class mixin 很适合解决您的问题。但最好不要在名称中使用"decorator",以免混淆。
我找到了一种实现某种多重继承的解决方案(真正将其级联),但值得一看。
假设您有一个具有一些属性和方法的 class 基础:
class Base {
tableName = 'My table name';
hello(name) {
return `hello ${name}`;
}
}
并且您想要 class 来扩展 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,打字稿语言服务将从这两个 class 中找到属性。它模拟了多重继承,但它实际上是一个级联。
class User extends UserFields(Base) { }
const u = new User();
u.tableName = 'users'; // ok
u.name = 'John'; // ok
通过这种方式,您可以将 UserFields 函数与任何其他 classes 重用。 一个明显的例子是,如果您想在客户端将 User 对象公开为 "clean" 对象并使字段可用,然后您有一个与数据库连接的 UserDb 对象,并且任何其他服务器端方法都可以具有同样的领域。我们只定义一次数据库字段!
另一个好处是你可以将 mixins mixin1(mixin2....(Base)) 链接起来,在同一个 class.
中拥有你想要的任意多的属性每个人都希望装饰器属性在 typescript 中可见,但同时也是一个很好的解决方案。