使用插件架构扩展类

时间:2019-10-29 02:43:13

标签: typescript

说我有一个带有静态方法Test的类.plugin,该方法带有一个函数。该函数可以运行任意代码并扩展Test的API。

const MyTest = Test.plugin(fooPlugin)
const test = new Test()
const myTest = new Test()
test.foo // does not exist
myTest.foo // exists

我做了a TypeScript Playground that I hope is close to working

在示例的末尾添加myTest.foo时,.foo键入为any。我希望<typeof plugin>返回我传递的plugin函数的类型,而不是通用规范?

如果我将<typeof plugin>替换为<typeof TestPlugin>,那么它将按预期工作。

在不改变插件体系结构工作方式的情况下,我能做些什么来做这项工作吗?

如果我稍微更改代码(Playground link),则myTest.foo的键入正确,但是有两个TypeScript错误。

1 个答案:

答案 0 :(得分:1)

您修改的方法几乎是正确的,只是T = Plugin是类型参数Plugin的默认值T,但是T可以是任何其他类型,不一定Plugin的子类型。您想说T extends Plugin,这意味着T必须是Plugin的子类型。

此外,您不需要Test中的索引签名(至少就插件体系结构而言)。这也会使丢失的成员成为错误(索引签名将隐藏这些成员):

type ApiExtension = { [key: string]: any }
export type Plugin = (instance: Test) => ApiExtension;
type Constructor<T> = new (...args: any[]) => T;

class Test {
  static plugins: Plugin[] = [];
  static plugin<T extends Plugin>(plugin: T) {
    const currentPlugins = this.plugins;

    class NewTest extends this {
      static plugins = currentPlugins.concat(plugin);
    }

    type Extension = ReturnType<T>
    return NewTest as typeof NewTest & Constructor<Extension>;
  }

  constructor() {
    // apply plugins
    // https://stackoverflow.com/a/16345172
    const classConstructor = this.constructor as typeof Test;
    classConstructor.plugins.forEach(plugin => {
      Object.assign(this, plugin(this))
    });
  }
}

// Question: how to make Typescript understand that MyTest instances have a .foo() method now?
type TestPluginExtension = {
  foo(): 'bar'
}

const TestPlugin = (test: Test): TestPluginExtension => {
  console.log('plugin evalutes')

  return {
    foo: () => 'bar'
  }
}
const MyTest = Test.plugin(TestPlugin)
const myTest = new MyTest()
myTest.foo()
myTest.fooo() //err

http://download.tensorflow.org/models/object_detection/