TypeScript:获取真实类中抽象方法实现的类型

时间:2019-06-06 10:04:40

标签: typescript

我有一个功能图,具有不同的签名

const allMyFunctions = {
  f1: () => Promise.resolve(1),
  f2: (a: number) => a
}; 

还有一个抽象类,使用这些功能可以做一些事情:

abstract class MyClass {
  protected abstract decorateMethod(...args: any[]): any;

  decorateAll() {
    return Object.keys(allMyFunctions)
      .reduce((acc, key) => {
        const f = allMyFunctions[key as keyof typeof allMyFunctions];

        acc[key as keyof typeof allMyFunctions] = this.decorateMethod(f);

        return acc;
      },{} as {[index in keyof typeof allMyFunctions]: ReturnType<MyClass['decorateMethod']>});
  }
}

因此,decorateAll使用相同的键创建了一个新映射,但是每个函数都经过decorateMethod,这是抽象的。

我现在可以实例化两个真实的类,如下所示:

class MyConstantClass extends MyClass {
  protected decorateMethod(f: AnyFunction) {
    return () => 2;
  }
}

class MyIdentityClass extends MyClass {
  protected decorateMethod<F extends AnyFunction>(f: F) {
    return f;
  }
}

const instance1 = new MyConstantClass();
const a = instance1.decorateAll().f1;

const instance2 = new MyIdentityClass();
const b = instance2.decorateAll().f2;

不幸的是,ab的类型是any,实际上它们应该是() => number(a:number) => number

看来,如果我在子类中复制粘贴decorateAll实现,并用ReturnType<MyIdentity['decorateMethod']>替换最后一行强制转换,那么它将起作用。但是这里有重复的逻辑,我想通过在第一位置使用抽象类来避免。

TS Playground

编辑:添加游乐场链接

2 个答案:

答案 0 :(得分:1)

您可以使用多态this来引用该类的当前类型。这将使您大致写出所需的内容

const allMyFunctions = {
  f1: () => Promise.resolve(1),
  f2: (a: number) => a
};

type AnyFunction = typeof allMyFunctions[keyof typeof allMyFunctions];

abstract class MyClass {
  abstract decorateMethod(...args: any[]): any;

  decorateAll() {
      return Object.keys(allMyFunctions)
          .reduce((acc, key) => {
              const f = allMyFunctions[key as keyof typeof allMyFunctions];

              acc[key as keyof typeof allMyFunctions] = this.decorateMethod(f);

              return acc;
          }, {} as {
              [index in keyof typeof allMyFunctions]: ReturnType<this['decorateMethod']>
          });
  }
}

class MyConstantClass extends MyClass {
  decorateMethod(f: AnyFunction) {
      return () => 2;
  }
}

class MyIdentityClass extends MyClass {
  decorateMethod<F extends AnyFunction>(f: F) {
      return f;
  }
}


const instance1 = new MyConstantClass();
const a = instance1.decorateAll().f1; //  () => number

const instance2 = new MyIdentityClass();
const b = instance2.decorateAll().f2; //  This might not be what you want: (() => Promise<number>) | ((a: number) => number)

MyConstantClass可以正常工作,它为所有函数() => number返回,但是MyIdentityClass返回对象中所有函数类型的并集。这是因为decorateMethod返回与原始内容相同的信息的类型未被捕获。 decorateMethod取得并并返回一个并,然后破坏类型。

答案 1 :(得分:0)

通过包含<T>类型来使您的抽象类通用,并使decorateMethod的结果为T(或()=>T)而不是any。然后,这两个具体的类将扩展,例如MyClass<number>(或MyClass<()=>number>)。这样可以解决您的至少一种情况。

对于第二种情况,您的decorateMethodF并返回F,这并不是那么容易,我想用您的decorateMethod来表达这一点实际上是不可能的。是通用的。我尝试执行此操作here,但是它不起作用-TS删除了泛型并返回了{}