首页 > 解决方案 > Typescript - 如何强制调用类方法?(强制方法)

问题描述

目标

我有一Logger堂课

class Logger {
  private logs = [];

  public log = (message: string) => {
    // append `message` to logs
  };

  public complete = () => {
    // log all `logs` to the console
  };
}

我想让这个complete方法成为强制性的。

这意味着complete必须在某个时候调用它。complete(具体来说,它应该是最后一个被调用的方法)如果永远不会被调用,则应该抛出一个错误。

例子

(1)

const l = new Logger();
l.log('start processing');
doSomething();
l.log('processing has been finished');
l.complete();

l.complete()将被调用,一切都很好✔️

(2)

const l = new Logger();
l.log('start processing');
doSomething();
l.log('processing has been finished');

l.complete()永远不会被调用,因此应该有一个错误✖️

(3)

const l = new Logger();
l.log('start processing');
doSomething();
l.log('processing has been finished');
if (someVariable) {
  l.complete();
}

l.complete()可能无法调用,请报错✖️

(4)

const l = new Logger();
l.log('start processing');
doSomething();
l.log('processing has been finished');
if (someVariable) {
  l.complete();
} else {
  l.complete();
}

l.complete()肯定会叫,一切都很好✔️

(5)

const l = new Logger();
l.log('start processing');
await doSomething();
l.log('processing has been finished');
l.complete();

l.complete()将被异步调用,一切都很好✔️

标签: typescript

解决方案


据我所知,编译器没有提供执行此操作的方法。我之前没有看到与此功能完全相同的副本,但它让我想起了一个类似的(参见microsoft/TypeScript#21388)要求制作它,以便您可以标记一个类方法,以便需要子类覆盖来调用超类方法。细节与您的问题不同,但它们都要求编译器在特定情况下未调用特定方法时发出警告。而且,据我所知,至少在 TypeScript 3.8 中,TypeScript 都没有办法做到这一点。所以你的问题的答案是“这是不可能的”。


也许不同的方法会起作用?与其要求Logger实例的用户作为最后一个方法调用,不如只在类似 Promise 的回调complete()中将一个实例交给用户,然后在回调完成时调用?这是“这是你的。请在你完成后打电话”和“告诉我你想用你的 做什么,然后我会在完成后打电话”之间的区别。这是一个可能的实现:Loggercomplete()Loggercomplete()Loggercomplete()

class Logger {

    private constructor() { }
    private logs: string[] = [];    
    public log(message: string) {
        this.logs.push(message)
    };

    private complete() {
        this.logs.forEach(l => console.log(l));
    };

    public static invoke<R>(cb: (logger: Logger) => R): R {
        const logger = new Logger();
        const r = cb(logger);
        if (r instanceof Promise) {
            const t = r.then(x => (logger.complete(), x)) as any as R;
            return t;
        } else {
            logger.complete();
            return r;
        }
    }
}

所以Logger构造函数和complete()方法都是私有的。唯一可以做的Logger就是调用invoke()它,它需要一个回调。在处理同步和异步回调方面存在一些问题,因为我想您希望同步回调被同步调用。但是在这两种情况下,回调都是用私有Logger实例调用的,然后complete()调用它的方法。

在您上面的示例中,现在无法制定 2 和 3(甚至 4)。complete()无论如何都会调用该方法。示例 1 如下所示:

Logger.invoke(l => {
    l.log('start SYNC processing');
    doSomething();
    l.log('SYNC processing has been finished');
})

示例 5 如下所示:

Logger.invoke(async l => {
    l.log('start ASYNC processing');
    await doSomething();
    l.log('ASYNC processing has been finished');
});

您可以验证它们是否都有效(使用 TypeScript Playground 时要小心,因为异步日志记录可能不会出现在“日志”窗格中,但它们应该出现在您的实际控制台日志中)。

Playground 代码链接


好的,希望有帮助;祝你好运!


推荐阅读