【问题标题】:Is there something like Lombok for TypeScript?TypeScript 有类似 Lombok 的东西吗?
【发布时间】:2020-03-26 21:49:50
【问题描述】:

我正在寻找一种方法来减少我的 NodeJS 后端的样板代码。在龙目岛有例如可以通过注解为对象注入构造函数和 getter/setter。

有没有办法在 TypeScript 中做到这一点?

【问题讨论】:

    标签: javascript node.js typescript lombok


    【解决方案1】:

    我快速搜索了一下,发现像 this 这样的项目试图为 TypeScript 带来类似 Lombok 的功能,但正如你所见,这些项目很少见,并且没有被广泛使用。这意味着一个问题:您为什么需要这样的工具?

    TS 在减少样板代码方面已经相当不错了。当我定义一个类时,我通常会这样做:

    class A {
      constructor(private fieldA: string, private readonly fieldB = 0) {}
    }
    

    这很简洁,不是吗?我猜您是在将 TS 的功能与 Java 的功能进行比较。 Java 非常冗长,Lombok 对此有很大帮助。但是 TS 和 JS 是不同的,虽然 Lombok 解决的一些问题已经被 TS 解决了,但其他问题在 TS 和 JS 的世界里都不是问题。

    首先,上面的语法创建具有访问修饰符的特定类型的类字段,您还可以在fieldB 前面发现readonly 关键字及其默认值0。最重要的是,它们与构造函数一起创建,构造函数在执行时隐式地为实例字段分配值(请参阅,没有this.fieldA = fieldA)。所以这已经不仅仅涵盖了 Lombok 注入构造函数的能力。请注意:在 JS 中(因此在 TS 中),您只能有一个构造函数。 JS 不支持方法重载。

    现在关于 getter/setter,它们在 JS(或 TS)中的使用方式与在 Java 中的使用方式不同。在 JS 中,直接使用字段是一种规范,setter 和 getter 仅在您想要的特殊情况下使用:

    1. 禁止在运行时通过仅定义一个 getter 来为对象的属性设置值。现在这通常有点矫枉过正,并且由于您使用 TS,您只需将该字段声明为 readonly,编译器将确保您不分配给该属性 - 无需使用吸气剂。如果您在没有编译时检查的情况下使用 JS 进行开发,则惯例是用下划线标记私有属性(您绝对不应该修改的属性)。无论哪种方式,您仍然可能修改您不应该修改的变量,但与 Java 不同,这不被认为是足以在 JS(和 TS)中随处使用 get/set 的理由。相反,如果您确实需要确定在运行时不会发生任何修改,您可以使用前面提到的不带 setter 的 getter,或者将对象的属性配置为不可写。
    2. 在 set/get 函数中具有自定义逻辑是使用它们的另一个好理由。一个常见的用例是一个由多个变量计算出来的 getter,但您仍然希望它看起来像对象上的一个字段。这是因为在 JS 中,当您调用 getter 时,实际上并没有在 getter 名称后使用 ()。现在因为这个逻辑是自定义的,所以不能仅仅通过注解来生成。

    如您所见,Lombok 在 Java 中处理的一些问题已经在 TS 中处理了,而另一些则不是问题。


    编辑 5-9-2021 - 回答 @Reijo 的问题: Lomboks 的功能超越了 getter/setter/constructors。查看@Builder 注释,我很想知道您对此有何看法。

    如果问题只是关于是否有一个 TypeScript/JavaScript 库提供或多或少与 Lombok for Java 相同的实用程序集合,据我所知,答案是否定的。我认为部分原因是 TypeScript 提供了开箱即用的功能(正如我在上面已经概述的那样),这让我回到了 Java 比 TypeScript 或 Groovy 等语言更需要 Lombok 的观点。当您需要 TS 不提供的东西时,例如构建器模式,您可以使用库来解决特定问题,例如 builder-pattern(在其核心中使用 JS 代理)或感谢 JS 的灵活特性(实际上是 TS ) 自己轻松编写。

    这一切都很好,但您可能希望以更具声明性的方式添加功能 - 通过注释(在 TS 世界中,它们被称为 decorators,它们的工作方式不同),就像 Lombok 所做的那样。这可能会很复杂。

    首先,如果您在 TS 中通过装饰器修改类型,TS 编译器将无法识别更改。因此,如果您通过在装饰器中添加一个方法来扩充类,TS 将无法识别该新方法。有关详细信息,请参阅此discussion

    这意味着您要么放弃装饰器并使用函数来修改类型(您可以),要么深入研究 AST。这就是龙目岛的工作方式。它在称为注释处理的编译阶段采用带注释的类型,并且由于 javac(和 Eclipse 编译器)中的 hack 修改了它们的 AST(例如,为给定的类创建内部构建器)。使用 TS/JS 可以采用类似的方式。

    虽然在 TS 和 JS 中没有像注释处理这样的东西,但您仍然可以创建一个构建步骤,该步骤获取源代码并修改它的 AST 以实现您的目标(这也是 Babel 的工作方式)。这可能会导致基于使用的注解(广义上 - 不一定是装饰器)向类添加方法、生成构建器等。

    不过,这种方法是一个挑战。除了 AST 是一个高级主题之外,即使你让它工作,你也需要你的 IDE 的支持,现在这也意味着语言服务器的支持。这不适合胆小的人。

    但是,如果您打算为 TS 创建像 Lombok 这样的东西,我的编辑不应该吓跑任何人,因为似乎有些人希望在 TS/JS 世界中看到它。它应该只向您展示前面的内容;)。

    【讨论】:

    • 在您的回答中,您专注于所询问的内容,但 Lomboks 的功能超越了 getters/setters/constructors。查看@Builder Annotation,我对您对此的看法很感兴趣。
    • @Reijo 对不起,伙计,直到现在才有时间。我编辑了我的答案。
    • 我也想到了用 Babel 修改 AST,但我看不到它与 TS 一起工作,因为必须以某种方式注入类型。装饰器也会影响性能,这会限制有用性。尽管您不想吓跑人们,但我想您在成本/努力与收益方面提出了一个很好的观点,因为 JS(和 TS)越来越转向函数式编程。无论如何,谢谢您的详细回答!
    • @Reijo 装饰器仅用作标记,因此可以在“注释处理”完成后将其删除。关于类型,我看不出有这样的问题,因为这个“注释处理”会在 TS 编译之前运行。您可能手头有自己的标准类型作为依赖项,例如。 Builder<T>,在类上遇到装饰器,为该类生成方法builder(),并为带注释的类生成实现 Builder 的实际构建器类。然后 TS 编译将使用修改后的源代码运行。不过这只是一个快速的想法,我还没有尝试过。
    猜你喜欢
    • 1970-01-01
    • 2017-07-15
    • 2014-09-22
    • 2010-11-29
    • 2011-10-18
    • 2023-03-09
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多