简体   繁体   English

如何为此 class 定义 Google Closure Compiler externs

[英]How to define the Google Closure Compiler externs for this class

I have the following JavaScript class definition that works as it should and that I compile using the Google Closure Compiler:我有以下 JavaScript class 定义可以正常工作,并且我使用 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;
  }
}

This type is part of the public interface of my code which means that I must prevent the Closure Compiler from renaming its symbols.这种类型是我的代码公共接口的一部分,这意味着我必须防止闭包编译器重命名它的符号。 I'm in the process of writing an externs file that I pass to the Closure Compiler using the --externs switch.我正在编写一个 externs 文件,我使用--externs开关将其传递给 Closure Compiler。 Here's what I have so far:这是我到目前为止所拥有的:

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() {}
};

I have three problems finishing that externs file:我在完成该 externs 文件时遇到了三个问题:

  1. The arguments of the constructor ( x , y , z , rotationX , rotationY ) are renamed.构造函数的 arguments ( x , y , z , rotationX , rotationY ) 被重命名。 What do I need to do to prevent that?我需要做些什么来防止这种情况发生?
  2. The static method fromUrlSearchParams(searchParams) is removed by the compiler because it comes to the conclusion that it is dead code because it is not used internally in the compiled code.来自fromUrlSearchParams(searchParams)static方法被编译器删除,因为它得出的结论是它是死代码,因为它没有在编译代码内部使用。 How do I export that static method?如何导出static方法?
  3. How do I mark the matrix property to be part of the public interface?如何将matrix属性标记为公共接口的一部分?

Edit:编辑:

After spending several hours with this problem, reading every piece of documentation I could find, crawling files on GitHub, testing various online externs generators and obtaining a copy of the book "Closure - The Definitive Guide" the problem is still unsolved.在花了几个小时解决这个问题后,阅读了我能找到的每一份文档,在 GitHub 上爬取文件,测试各种在线外部生成器并获得了《Closure - The Definitive Guide》一书的副本,但问题仍未解决。

After being around for more than a decade the Closure Compiler documentation is still useless for everything more than the most basic examples. Closure Compiler 文档在出现了十多年之后,除了最基本的示例之外,仍然对所有内容都毫无用处。 You got to be kidding me.你一定是在开玩笑吧。

Here's what I tried so far:这是我到目前为止所尝试的:

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() {}
};

The difference to the original file is using class State { instead of State = class { .与原始文件的不同之处在于使用class State {而不是State = class { Interestingly, this results in the following error message:有趣的是,这会导致以下错误消息:

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

No clue why that would make a difference, but anyway, let's move on.不知道为什么这会有所作为,但无论如何,让我们继续前进。 Next attempt:下一次尝试:

/**
 * @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() {};

Running it with that code results in使用该代码运行它会导致

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

class State {
      ^^^^^

Well, here we go again.好吧,我们再来一次 go。 It is a mystery to me why the compiler would state that it is already defined if I pass a source file and an extern file.如果我传递一个源文件和一个外部文件,编译器为什么会 state 已经定义,这对我来说是个谜。 One defines it, the other annotates it, or so you would think.一个定义它,另一个注释它,或者你会这么想。

No attempt could save the static method from being removed by the compiler.没有任何尝试可以避免 static 方法被编译器删除。

Next to building and debugging the compiler with my code I don't see anything else that I could try.在使用我的代码构建和调试编译器之后,我没有看到其他可以尝试的方法。 Luckily there is a guaranteed resolution for the problem: Simply not using the Google Closure Compiler.幸运的是,这个问题有一个保证的解决方案:只是不使用 Google Closure Compiler。

I'm no expert in this, but here are some things to try.我不是这方面的专家,但这里有一些东西可以尝试。 See also https://github.com/google/closure-compiler/wiki/JS-Modules另请参阅https://github.com/google/closure-compiler/wiki/JS-Modules

At the end of your file you might try adding在文件末尾,您可以尝试添加

exports = State.出口 = State。

You might also need to add at the top:您可能还需要在顶部添加:

goog.module('State'); goog.module('状态');

  1. Why do you need to prevent the arguments of the constructor being renamed?为什么需要防止构造函数的arguments被重命名? These aren't visible outside the constructor, so should not be a problem.这些在构造函数之外是不可见的,所以应该不是问题。

  2. Try this:尝试这个:

goog.exportSymbol('fromUrlSearchParams', fromUrlSearchParams); goog.exportSymbol('fromUrlSearchParams', fromUrlSearchParams);

  1. JavaScript getter and setter properties might not be supported in the compiler.编译器可能不支持 JavaScript getter 和 setter 属性。 See this https://google.github.io/styleguide/jsguide.html#features-classes-getters-and-setters请参阅此https://google.github.io/styleguide/jsguide.html#features-classes-getters-and-setters

The following worked:以下工作:

Input file:输入文件:

"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;
  }
}

Externs file:外部文件:

/**
 * @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() {}
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM