如何在 Typescript 中以编程方式创建类函数?

Tom*_*ost 3 typescript

我有一个日志类,需要定义各种便利函数,例如errorwarninfo等,它们都执行相同的操作:调用函数log及其严重性和要记录的数据。

这很冗长并且浪费知识库:

class Logger {
  error(...args: any[]): void {
    this.log('error', ...args)
  }
  warn(...args: any[]): void {
    this.log('warn', ...args)
  }
  // ...and so on
}
Run Code Online (Sandbox Code Playgroud)

我目前有所有严重性的枚举,以及所有严重性的字符串的联合:

enum severityLevels {
  error,
  warn,
  info
}

type severities = keyof typeof severityLevels
Run Code Online (Sandbox Code Playgroud)

我想做的是以编程方式在记录器类的构造函数中为每个严重级别创建函数(Object.keys(severityLevels).forEach(...)在普通 JS 中非常简单),但是我不知道如何让 TypeScript 允许这种情况发生。如果我用所有严重性函数定义创建一个接口,我就做不到implements(这些函数尚不存在)。如果我将它们设为可选,那么调用这些日志函数(“可能不存在”)会变得混乱。

有任何想法吗?

jca*_*alz 8

您可以通过编程方式向 JavaScript 中的构造函数添加方法prototype,从而在 TypeScript 中添加方法。棘手的部分是让类型系统理解你在做什么,这是可行的,但需要一些类型断言

首先,让我们列出这些严重程度的实际运行时间列表:

const severityKeys =
  Object.keys(SeverityLevels).filter(k => k !== (+k) + "") as Severities[];
Run Code Online (Sandbox Code Playgroud)

请注意,根据 TS 约定,我将类型初始化为SeverityLevels和。Severities另请注意,enumTypeScript 中的数字对象也具有反向映射:Severities.erroris0Severities[0]is "error"。要获取仅包含键的列表,我必须使用 过滤掉“数字”键k !== (+k)+""

然后,让我们创建一个示例BaseLogger类来表示记录器的功能,而无需添加方法:

class BaseLogger {
  constructor(public loggerName: string) {

  }
  log(level: string, ...args: any[]) {
    console.log("[" + this.loggerName + "]", "[" + level + "]", ...args);
  }
}
Run Code Online (Sandbox Code Playgroud)

以下是我们如何进行编程扩展:

type Logger = BaseLogger & Record<Severities, (...args: any) => void>;
const Logger = (
  class Logger extends BaseLogger { }
) as new (...args: ConstructorParameters<typeof BaseLogger>) => Logger;
severityKeys.forEach(k => Logger.prototype[k] = function (this: Logger, ...args: any) {
  this.log(k, ...args);
})
Run Code Online (Sandbox Code Playgroud)

这将创建一个名为 的类型和一个值Logger,因此它可以像普通的class. 该类型对应于实例的类型 Logger,它看起来就像BaseLogger添加的方法属性,其键为Severities,值为(...args: any[])=>void

该值Logger是实例的构造Logger函数,它以空扩展开始BaseLogger(尽管我们断言它构造Logger实例)。之后我们根据需要severityKeys.forEach()添加实际的方法。Logger.prototype

让我们看看它是否有效:

const logger = new Logger("My Logger");
logger.error(123); // [My Logger] [error] 123
logger.info(456); // [My Logger] [info] 456
logger.warn(789); // [My Logger] [warn] 789
Run Code Online (Sandbox Code Playgroud)

在我看来很好。好吧,希望有帮助;祝你好运!

链接到代码