搜索词
装饰器,元编程,重命名,重命名
建议
作为接近第三阶段的装饰器,由于其动态性质,可能会出现一些TypeScript无法处理的使用场景。添加的任何额外属性都不会被看到,重命名的属性不会被分析,而受保护或朋友等自定义可见性方案也不会被理解。
这是JavaScript动态特性可能伸展TypeScript类型系统的一个领域,但通过Map和条件类型,我们可能能够描述类型系统中的一部分使用场景。
我没有一个具体的解决方案可以建议,但一个解决方案应该满足的一些要求:
- 描述装饰属性的类型变化
- 描述属性名称的变化
- 描述新引入的属性(例如,
@observed
属性的私有存储) - 描述类的变化(例如,添加静态属性)
Map和条件类型已经支持一些类型转换,但不能转换键,或者描述静态和示例类型的转换。
也许有一种方法可以扩展Map类型的列表推导式语法,使其更像列表推导式,并允许:
- 属性名称的转换
- 过滤
以下是一个大致的想法,将键名转换为符号的Map,并通过带有特定装饰器的属性进行过滤:
type Namespaced<T> = {
[Symbol(P): T[P] for P in keyof T where namespaced decorates P];
}
Symbol(P)
和 where namespaced decorates P
显然是非常牵强的,可能也不是很好的选择。
这对于来自单个装饰器的单独使用来说并不完全正确。因为装饰器接受并返回一个扩展的PropertyDescriptor,所以我们可能只需要指定装饰器本身:
const namespaced = <P extends PropertyDescriptor>(descriptor: P): NamespacedPropertyDescriptor<P> => { ... }
type NamespacedPropertyDescriptor<P extends PropertyDescriptor> = {
key: Symbol(P['key']);
} extends P;
我抛出了 extends
类型操作符,因为 key
必须覆盖 key
在 P
中,所以交集不起作用。某种类似对象展开的语法也可能起作用)
这描述了属性键的转换,但它没有将其与该符号的声明关联起来。
一种可能性是让TypeScript理解属性描述符上的 finisher
方法的类型,并使用它来推断对类本身的更改:
// P is the PropertyDescriptor passed to the decorator
// C is the constructor type for static properties, there would need to be a instance type
// as well
type NamespacedPropertyDescriptor<P extends PropertyDescriptor, C> = {
key: typeof C[P['key']];
finisher(klass: C): NamespacedClass<C, P['key']>;
};
type NamespacedClass<C, K> = {
readonly K: symbol;
} extends C;
我知道我只是在那里提出了许多模糊、复杂、可能不可行的想法,但也许有一些更好的解决方案方向(除了添加命令式类型语言),这些想法可以激发灵感。
另一个方法是让TypeScript内在地理解一些众所周知的装饰器的工作原理,而不是在其类型系统中允许它们的定义。
2条答案
按热度按时间yyhrrdl81#
在之前的讨论中,我们提到了 #4881。
v7pvogib2#
@RyanCavanaugh 我很好奇装饰器是否可以用于实现某些接口所需的缺失方法。
我的应用程序有一些RTTI系统,它具有这个接口:
在编写实现时,你需要编写很多样板代码:
我真的希望装饰器至少可以将这些代码减少到这种程度: