Javascript 如何为用typescript编写的模块编写插件加载程序

Javascript 如何为用typescript编写的模块编写插件加载程序,javascript,typescript,Javascript,Typescript,我正在处理一个模块,作为启动的一部分,它将根据配置文件动态加载其他模块,配置文件提供要导入的包或文件的名称。我用vanilla JS写了一些不错的东西,但我正在努力将它翻译成打字脚本 在Javascript中,我正在做: ./src/main.js(假设config.plugins=['./plugin']): ./src/plugin.js: class Plugin() { } module.exports = (main_module) => { main_module.plu

我正在处理一个模块,作为启动的一部分,它将根据配置文件动态加载其他模块,配置文件提供要导入的包或文件的名称。我用vanilla JS写了一些不错的东西,但我正在努力将它翻译成打字脚本

在Javascript中,我正在做:

./src/main.js(假设
config.plugins=['./plugin']
):

./src/plugin.js:

class Plugin() {
}

module.exports = (main_module) => {
  main_module.plugin = new Plugin();
}
尝试将其转换为Typescript让我明白了(主要的区别是插件现在是异步加载的,这是一个问题,但至少可以编译,所以这里不是我的重点):

./src/main.ts:

interface Config {
  plugins: string[];
}

interface Plugin {
  register(main_module: ModuleName): void;
}

export class ModuleName {
  public config: Config;
  public constructor(config: Config) {
    this.config = config;
  }

  public async init(): Promise<string[]> {
    return this.loadPlugins(this.config.plugins);
  }

  // You don't need to return anything from here because `async` functions
  // always return a `Promise`.
  private async loadPlugins(plugins: string[]): Promise<string[]> {
    for (let plugin of plugins) {
      // Here using `async`/`await` is a lot less complicated than using
      // `then()` because of the `for...of` loop
      let loaded: Plugin = await import(plugin);
      loaded.register(this);
    }
    return plugins;
  }
}
现在,在main.ts中,它允许我自由引用
这个.plugin
。但是,当我尝试将其导入到项目中时,我不会引用该插件

例如,将上述内容编译到
/dist
文件夹,并具有以下内容(在我的项目根目录下):

/test.ts

import { ModuleName } from './dist';

let a = new ModuleName({plugins: ['./plugin']});
a.init().then(a.plugin.foo()).catch(console.error);

tsc指出类型“ModuleName”上不存在属性“plugin”时出错。

看起来您正在异步加载它,并且可能试图过快地访问它。使用
setTimeout
来确保在需要时完成异步进程绝对不是一个好主意(如果进程在速度快的计算机上需要50毫秒,但在速度慢的计算机上需要500毫秒怎么办?);相反,您希望直接访问
Promise
,以便可以使用
Promise.then()
。问题是,您永远不希望从构造函数返回对象本身以外的任何内容,因此您永远不希望使用异步构造函数。更好的模式是使用单独的初始化函数来执行异步过程

此外,
import(plugin)
返回一个
Promise
,而不是插件本身。因此,您还必须相应地处理此问题,并等待解析由
loadPlugins
返回的
Promise
,直到所有
import
s解析完成。这可能不是一个很容易阅读的语句,因此这里有一些代码可以比我更好地解释它:

export class ModuleName {
  private config: Config;

  public constructor(config: Config) {
    this.config = config;
  }

  public async init(): Promise<void> {
    return this.loadPlugins(this.config.plugins);
  }

  // You don't need to return anything from here because `async` functions
  // always return a `Promise`.
  private async loadPlugins(plugins: string[]): Promise<void> {
    for (let plugin of plugins) {
      // Here using `async`/`await` is a lot less complicated than using
      // `then()` because of the `for...of` loop
      let loaded: Plugin = await import(plugin);
      loaded.register(this);
    }
  }
}
或者,即使这样也能奏效:

设a=newmodulename(配置);
a、 init().then(a.plugin.foo).catch(console.error)

虽然这是一种更好的异步加载插件的方法,但这并不能解决更大的问题,即在导入模块时,由于TS抛出错误,我仍然无法引用任何插件。如果您按照我这里的方式更新
main.TS
,它应该可以工作(至少对我有效)。是否将配置对象传递给
newmoduleName()?在
plugin.ts
中,您有
main\u module:module
,但它应该是
main\u ModuleName:ModuleName
。这是一个输入错误。我已经使用您对async的建议更新了我的问题,并显示了my main.ts的外观/位置以及它是如何引用模块的。就像我说的,如果我把上面的类instantation放在./src/index.ts中,它可以工作,但是放在./test.ts(它引用模块的编译版本)中,没有骰子。此外,这只是假设插件是从与index.ts相同的文件夹加载的。当插件从say
@name/module-plugin1加载时会发生什么?啊,我想我明白了。谢谢您的帮助。您需要导出插件
import { ModuleName } from './dist';

let a = new ModuleName({plugins: ['./plugin']});
a.init().then(a.plugin.foo()).catch(console.error);
export class ModuleName {
  private config: Config;

  public constructor(config: Config) {
    this.config = config;
  }

  public async init(): Promise<void> {
    return this.loadPlugins(this.config.plugins);
  }

  // You don't need to return anything from here because `async` functions
  // always return a `Promise`.
  private async loadPlugins(plugins: string[]): Promise<void> {
    for (let plugin of plugins) {
      // Here using `async`/`await` is a lot less complicated than using
      // `then()` because of the `for...of` loop
      let loaded: Plugin = await import(plugin);
      loaded.register(this);
    }
  }
}