TypeScript 分析支持元编程装饰器

50pmv0ei  于 9个月前  发布在  TypeScript
关注(0)|答案(2)|浏览(133)

搜索词

装饰器,元编程,重命名,重命名

建议

作为接近第三阶段的装饰器,由于其动态性质,可能会出现一些TypeScript无法处理的使用场景。添加的任何额外属性都不会被看到,重命名的属性不会被分析,而受保护或朋友等自定义可见性方案也不会被理解。
这是JavaScript动态特性可能伸展TypeScript类型系统的一个领域,但通过Map和条件类型,我们可能能够描述类型系统中的一部分使用场景。
我没有一个具体的解决方案可以建议,但一个解决方案应该满足的一些要求:

  • 描述装饰属性的类型变化
  • 描述属性名称的变化
  • 描述新引入的属性(例如,@observed属性的私有存储)
  • 描述类的变化(例如,添加静态属性)

Map和条件类型已经支持一些类型转换,但不能转换键,或者描述静态和示例类型的转换。
也许有一种方法可以扩展Map类型的列表推导式语法,使其更像列表推导式,并允许:

  • 属性名称的转换
  • 过滤

以下是一个大致的想法,将键名转换为符号的Map,并通过带有特定装饰器的属性进行过滤:

  1. type Namespaced<T> = {
  2. [Symbol(P): T[P] for P in keyof T where namespaced decorates P];
  3. }

Symbol(P)where namespaced decorates P 显然是非常牵强的,可能也不是很好的选择。
这对于来自单个装饰器的单独使用来说并不完全正确。因为装饰器接受并返回一个扩展的PropertyDescriptor,所以我们可能只需要指定装饰器本身:

  1. const namespaced = <P extends PropertyDescriptor>(descriptor: P): NamespacedPropertyDescriptor<P> => { ... }
  2. type NamespacedPropertyDescriptor<P extends PropertyDescriptor> = {
  3. key: Symbol(P['key']);
  4. } extends P;

我抛出了 extends 类型操作符,因为 key 必须覆盖 keyP 中,所以交集不起作用。某种类似对象展开的语法也可能起作用)
这描述了属性键的转换,但它没有将其与该符号的声明关联起来。
一种可能性是让TypeScript理解属性描述符上的 finisher 方法的类型,并使用它来推断对类本身的更改:

  1. // P is the PropertyDescriptor passed to the decorator
  2. // C is the constructor type for static properties, there would need to be a instance type
  3. // as well
  4. type NamespacedPropertyDescriptor<P extends PropertyDescriptor, C> = {
  5. key: typeof C[P['key']];
  6. finisher(klass: C): NamespacedClass<C, P['key']>;
  7. };
  8. type NamespacedClass<C, K> = {
  9. readonly K: symbol;
  10. } extends C;

我知道我只是在那里提出了许多模糊、复杂、可能不可行的想法,但也许有一些更好的解决方案方向(除了添加命令式类型语言),这些想法可以激发灵感。
另一个方法是让TypeScript内在地理解一些众所周知的装饰器的工作原理,而不是在其类型系统中允许它们的定义。

yyhrrdl8

yyhrrdl81#

在之前的讨论中,我们提到了 #4881

v7pvogib

v7pvogib2#

@RyanCavanaugh 我很好奇装饰器是否可以用于实现某些接口所需的缺失方法。
我的应用程序有一些RTTI系统,它具有这个接口:

  1. interface ITypeable {
  2. readonly typeRep: TypeRep<any>;
  3. is<T>(t:TypeRep<T>): this is T
  4. }

在编写实现时,你需要编写很多样板代码:

  1. const PrimGlyph_T = new TypeRep<PrimGlyph>("CLSID.PrimGlyph", GEL.IGlyph_T);
  2. class PrimGlyph implements GEL.IGlyph {
  3. public readonly typeRep = PrimGlyph_T
  4. is<T>(t: TypeRep<T>) { return t.sub(PrimGlyph_T); }
  5. }

我真的希望装饰器至少可以将这些代码减少到这种程度:

  1. @RTTI("CLSID.PrimGlyph", GEL.IGlyph_T)
  2. class PrimGlyph implements GEL.IGlyph {
  3. }
展开查看全部

相关问题