【问题标题】:How to define the Google Closure Compiler externs for this class如何为此类定义 Google Closure Compiler externs
【发布时间】:2021-04-12 19:57:06
【问题描述】:

我有以下 JavaScript 类定义,它可以正常工作,并且我使用 Google Closure Compiler 进行编译:

class State {
  constructor(x, y, z, rotationX, rotationY) {
    this.x = x;
    this.y = y;
    this.z = z;
    this.rotationX = rotationX;
    this.rotationY = rotationY;
  }

  set matrix(value) {
    // Magic
  }

  get matrix() {
    // More magic
  }

  set red(value) {
    this.setAttribute(attributeRed, value)
  }

  get red() {
    return this.getAttribute(attributeRed);;
  }

  static fromUrlSearchParams(searchParams) {
    return new State(parseInt  (searchParams.get("x"), 10),
                     parseInt  (searchParams.get("y"), 10),
                     parseInt  (searchParams.get("z"), 10),
                     parseFloat(searchParams.get("rotationX")),
                     parseFloat(searchParams.get("rotationY")));
  }

  toUrlSearchParams() {
    let searchParams = new URLSearchParams();
    searchParams.set("x", this.red);
    searchParams.set("y", this.green);
    searchParams.set("z", this.blue);
    searchParams.set("rotationX", this.pitch);
    searchParams.set("rotationY", this.yaw);
    return searchParams;
  }
}

这种类型是我的代码公共接口的一部分,这意味着我必须防止闭包编译器重命名它的符号。我正在编写一个外部文件,我使用--externs 开关将其传递给闭包编译器。到目前为止,这是我所拥有的:

State = class {
  /**
   * @constructor
   * @param {number} x
   * @param {number} y
   * @param {number} z
   * @param {number} rotationX
   * @param {number} rotationY
   */
  constructor(x, y, z, rotationX, rotationY) {
    /** @type {number} */
    this.x = x;
    /** @type {number} */
    this.y = y;
    /** @type {number} */
    this.z = z;
    /** @type {number} */
    this.rotationX = rotationX;
    /** @type {number} */
    this.rotationY = rotationY;
  }

  // Insert property export here. If you just knew how...

  /** @return {State} */
  static fromUrlSearchParams(searchParams) {}

  /** @return {URLSearchParams} */
  toUrlSearchParams() {}
};

我在完成那个 externs 文件时遇到了三个问题:

  1. 构造函数的参数(xyzrotationXrotationY)被重命名。我需要做些什么来防止这种情况发生?
  2. static 方法fromUrlSearchParams(searchParams) 被编译器删除,因为它得出的结论是它是死代码,因为它没有在编译代码内部使用。如何导出 static 方法?
  3. 如何将matrix 属性标记为公共接口的一部分?

编辑:

在解决这个问题几个小时后,阅读了我能找到的所有文档,在 GitHub 上爬取文件,测试各种在线外部生成器,并获得了《Closure - The Definitive Guide》一书的副本"问题仍未解决。

在存在了十多年之后,Closure Compiler 文档对于除了最基本的示例之外的所有内容仍然毫无用处。你一定是在开玩笑吧。

到目前为止,这是我尝试过的:

class State {
  /**
   * @constructor
   * @param {number} x
   * @param {number} y
   * @param {number} z
   * @param {number} rotationX
   * @param {number} rotationY
   */
  constructor(x, y, z, rotationX, rotationY) {
    /** @type {number} */
    this.x = x;
    /** @type {number} */
    this.y = y;
    /** @type {number} */
    this.z = z;
    /** @type {number} */
    this.rotationX = rotationX;
    /** @type {number} */
    this.rotationY = rotationY;
  }

  /**
   * @nocollapse
   * @param {URLSearchParams} searchParams
   * @return {State}
   */
  static fromUrlSearchParams(searchParams) {}

  /** @return {URLSearchParams} */
  toUrlSearchParams() {}
};

与原始文件的区别在于使用class State { 而不是State = class {。有趣的是,这会导致以下错误消息:

ERROR - [JSC_BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR] Duplicate let / const / class / function declaration in the same scope is not allowed.

不知道为什么会有所作为,但无论如何,让我们继续前进。下次尝试:

/**
 * @constructor
 * @param {number} x
 * @param {number} y
 * @param {number} z
 * @param {number} rotationX
 * @param {number} rotationX
 */
var State = {};

/** @type {number} */
State.prototype.x;

/** @type {number} */
State.prototype.y;

/** @type {number} */
State.prototype.z;

/** @type {number} */
State.prototype.rotationX;

/** @type {number} */
State.prototype.rotationX;

/**
 * @nocollapse
 * @param {URLSearchParams} searchParams
 * @return {State}
 */
State.fromUrlSearchParams = function(searchParams) {};

/** @return {URLSearchParams} */
State.prototype.toUrlSearchParams = function() {};

使用该代码运行它会导致

ERROR - [JSC_VAR_MULTIPLY_DECLARED_ERROR] Variable ColorCubeState declared more than once. First occurrence: blabla.js

class State {
      ^^^^^

好吧,我们又来了。如果我传递一个源文件和一个外部文件,为什么编译器会声明它已经定义,这对我来说是个谜。一个定义它,另一个注释它,或者你会这么想。

没有任何尝试可以避免静态方法被编译器删除。

在使用我的代码构建和调试编译器之后,我没有看到其他可以尝试的方法。幸运的是,该问题有保证的解决方案:根本不使用 Google Closure Compiler。

【问题讨论】:

    标签: javascript google-closure-compiler


    【解决方案1】:

    以下工作:

    输入文件:

    "use strict";
    
    const attributeX         = "x";
    const attributeY         = "y";
    const attributeZ         = "z";
    const attributeRotationX = "rotationX"
    const attributeRotationY = "rotationY";
    
    /** @implements {StateInterface} */
    globalThis["State"] = class {
      constructor(x, y, z, rotationX, rotationY) {
        this.x         = x;
        this.y         = y;
        this.z         = z;
        this.rotationX = rotationX;
        this.rotationY = rotationY;
      }
    
      /**
       * @nocollapse
       * @suppress {checkTypes}
       */
      static "fromUrlSearchParams"(searchParams) {
        return new globalThis["State"](parseInt  (searchParams.get(attributeX), 10),
                                       parseInt  (searchParams.get(attributeY), 10),
                                       parseInt  (searchParams.get(attributeZ), 10),
                                       parseFloat(searchParams.get(attributeRotationX)),
                                       parseFloat(searchParams.get(attributeRotationY)));
      }
    
      toUrlSearchParams() {
        let searchParams = new URLSearchParams();
        searchParams.set(attributeX        , this.x        .toString(10));
        searchParams.set(attributeY        , this.y        .toString(10));
        searchParams.set(attributeZ        , this.z        .toString(10));
        searchParams.set(attributeRotationX, this.rotationX.toString(10));
        searchParams.set(attributeRotationY, this.rotationY.toString(10));
        return searchParams;
      }
    }
    

    外部文件:

    /**
     * @fileoverview
     * @externs
     */
    
    /** @interface */
    class StateInterface {
      /**
       * @param {number} x
       * @param {number} y
       * @param {number} z
       * @param {number} rotationX
       * @param {number} rotationY
       */
      constructor(x, y, z, rotationX, rotationY) {
        /** @type {number} */
        this.x = x;
        /** @type {number} */
        this.y = y;
        /** @type {number} */
        this.z = z;
        /** @type {number} */
        this.rotationX = rotationX;
        /** @type {number} */
        this.rotationY = rotationY;
      }
    
      /**
       * @param {URLSearchParams} searchParams
       * @return {StateInterface}
       */
      static fromUrlSearchParams(searchParams) {}
    
      /** @return {URLSearchParams} */
      toUrlSearchParams() {}
    };
    

    【讨论】:

      【解决方案2】:

      我不是这方面的专家,但这里有一些可以尝试的方法。也可以看看 https://github.com/google/closure-compiler/wiki/JS-Modules

      您可以尝试在文件末尾添加

      出口 = 状态。

      您可能还需要在顶部添加:

      goog.module('State');

      1. 为什么要防止构造函数的参数被重命名?这些在构造函数之外是不可见的,所以应该不是问题。

      2. 试试这个:

      goog.exportSymbol('fromUrlSearchParams', fromUrlSearchParams);

      1. 编译器可能不支持 JavaScript getter 和 setter 属性。看到这个 https://google.github.io/styleguide/jsguide.html#features-classes-getters-and-setters

      【讨论】:

      • 关于我为什么要阻止构造函数参数被重命名:读取源代码的 IDE 等工具将看到这些符号并可以将这些名称呈现给用户,这有助于理解构造函数的含义预计。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-12
      • 2016-07-06
      • 2015-07-27
      • 2016-08-15
      • 2014-10-15
      • 2011-05-12
      相关资源
      最近更新 更多