【问题标题】:Typescript Decorators: Add instance field to class to use within the same class打字稿装饰器:将实例字段添加到类以在同一类中使用
【发布时间】:2020-07-09 19:07:52
【问题描述】:

我目前正在学习 Typescript 装饰器。我的第一个目标是在一定程度上将 @Slf4J 从 Project Lombok 在 Java 中所做的复制到 Typescript。想法是用例如注释/装饰一个类。 @logger 在同一类中接收 LogUtil 类型的字段 log 以便调用例如log.info().

LogUtil 类:

export class LoggerUtil {
    logLevel: LogLevel;

    constructor(logLevel: LogLevel) {
        this.logLevel = logLevel;
    }

    error(className: string, message: string) {
        if (this.logLevel >= LogLevel.ERROR) {
            console.error(`${new Date()} [ERROR] ${className}: ${message}`);
        }
    }

    warn(className: string, message: string) {
        if (this.logLevel >= LogLevel.WARN) {
            console.log(`${new Date()} [WARN] ${className}: ${message}`);
        }
    }

    log(className: string, message: string): void {
        console.log(`${new Date()} [LOG] ${className} ${message}`)
    }

    info(className: string, message: string): void {
        if (this.logLevel >= LogLevel.INFO) {
            console.log(`${new Date()} [INFO] ${className}: ${message}`)
        }
    }

    call(className: string, message: string) {
        if (this.logLevel >= LogLevel.INFO) {
            console.log(`${new Date()} [CALL] ${className}.${message}`)
        }
    }

    debug(className: string, message: string) {
        if (this.logLevel >= LogLevel.DEBUG) {
            console.log(`${new Date()} [DEBUG] ${className}: ${message}`)
        }
    }
}

LogLevel 枚举:

export enum LogLevel {
    ERROR = 0,
    WARN = 1,
    INFO = 2,
    DEBUG = 3
}

使用@logger 装饰器获取LoggerUtil 实例作为日志的示例类

@logger
export class SomeService {

    exampleFunction() {
        log.info("exampleFunction called")
    }
}

我目前正在尝试使用类级别的装饰器来做到这一点。在这里我尝试做不同的事情:

使用 Reflect API 定义类的属性。在这里我什至不确定这是否有效。

export function logger() {
    return function(target: Function) {
        Reflect.defineProperty(target, "log", { value: new LoggerUtil(LogLevel.DEBUG) } )
    }
}

使用类原型定义属性:

export function logger() {
    return function(target: Function) {
        target.prototype.log = new LoggerUtil(LogLevel.DEBUG);
    }
}

在引用服务中的日志实例时,我得到“找不到名称 'log'”的每一种方法:

@logger
export class SomeService {

    exampleFunction() {
        log.info("exampleFunction called") // Cannot find name 'log'
    }
}

我的想法可能吗?我缺少什么基本的东西吗?

非常感谢您提前提供任何反馈!

【问题讨论】:

  • 谢谢!我觉得该解决方案不适用于我的情况,因为我想在同一个类中使用方法/字段而不先创建实例。因此,据我所知,我无法利用继承。

标签: javascript typescript logging decorator lombok


【解决方案1】:

我找到了一种至少可以满足我一点需求的方法:

我选择了继承(我想避免)。见下例:

@logger
export class SomeService extends Logger {

    constructor() {
        super()
    }

    exampleFunction() {
        log.info("exampleFunction called")
    }
}

那么我正在扩展的超类是这样的:

@logger
export class Logger {
    log: LoggerUtil;
}

最后,装饰器看起来像这样:

export function logger<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        log = new LoggerUtil(LogLevel.DEBUG);
    }
}

我的想法是通过继承将字段 log 添加到 SomeService。现在要初始化这个字段,我在超类上使用了装饰器。装饰器本身返回一个类,该类使用初始化字段扩展装饰器类。这样就创建了以下继承图:

@logger(装饰器)-> Logger(超类)-> SomeService。

我本可以在超类Logger 中初始化log 字段。然而,这是为了研究装饰器并希望从长远来看消除超类。

作为参考,我想指出Typescript documentation 关于如何覆盖构造函数。

【讨论】:

    猜你喜欢
    • 2020-05-31
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    • 1970-01-01
    • 2018-02-12
    • 2020-01-02
    • 2017-04-02
    相关资源
    最近更新 更多