使用装饰器强制类的所有方法具有特定签名

时间:2019-12-20 13:16:00

标签: typescript types

我正在编写一个装饰器,该装饰器将类实例作为参数接收,并且将需要此类的所有方法来返回它。我遇到的问题是在类实例中还有其他不符合此签名的字段(即newprototype)。

我该如何解决?这就是我所拥有的:

我要使用的装饰器的用途:

@Migrates(ProductCreated)
class EventMigration {
  public bar (a: Record<string, any>): ProductCreated {
    // ...
  }

  public foo (a: Record<string, any>): ProductCreated {
    // ...
  }
}

装饰器定义:

interface Class<T> {
  new (...args: any[]): T
}

type Foo<T, V> = {
  [P in keyof T]
  : T[P] extends (old: Record<string, any>) => V
  ? 'it works!'
  : 'nope!'
}

function Migrates<T, V>(y: Class<V>) {
  return (x: Foo<T, V>): void => {}
}

理论上,如果其中一种方法的签名与Foo中定义的签名不同,则应该失败,但如果这样做,则不会出现编译错误。

1 个答案:

答案 0 :(得分:0)

这最终是TypeScript闭包的怪异行为。如果我在返回类型中指定Migrates的类型参数,它将起作用。

以这种方式解决:

// Required to tell the compiler that it has atleast a constructor
export interface Class<TReflected> {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  new (...args: any[]): TReflected
}

// Constrains the properties to have the same type
type HomogenizePropertyTypes<Type, TPropertyType> = {
  [K in keyof Type]: TPropertyType
}

// The type that we want to use for our methods
type MigrationMethod<TConcept> = (x: Record<string,any>) => TConcept

// This type constrains the properties of the first parameter to be of
// the type MigrationMethod, the return type is specified by the second parameter
type Migration<TMigration, TConcept> = HomogenizePropertyTypes<TMigration, MigrationMethod<TConcept>>

// The decorator function, it is curried, accepts a class that constrains the
// return type of the methods, and then the class that it will constrain at
// the type level. After that, we can do whatever we want with it.
function Migrates<TConcept>(
  concept: Class<TConcept>
): <TMigration>(
  k: Class<Migration<TMigration, TConcept>>
) => void {
  return (klass) => {
    return {} as any   // Here we would do the rest of the stuff we want
  }
}