如何让闭包编译器正确检查 javascript 中构造函数的属性

发布于 2024-12-05 01:53:33 字数 1000 浏览 0 评论 0原文

我无法理解如何让 javascript 的 google 闭包编译器正确识别构造函数的属性。例如,在下面的代码中,我不知道第 27 行中的 'cls' 是什么类型,这样编译器就不会产生以下(我认为是错误的)错误:

foo.js:29: WARNING - Property say从未在 cls 上定义 cls.say(); ^

从阅读文档看来我应该像 25 中那样做。有人可以帮忙吗?谢谢,

 1  /**
 2   * @interface
 3   **/
 4  function Sayer() {}
 5  Sayer.prototype.say = function() {};
 6  Sayer.say = function() {};
 7  
 8  /**
 9   * @constructor
10   * @implements {Sayer}
11   **/
12  function A() {}
13  A.say = function() { console.log('A factory'); };
14  A.prototype.say = function() { console.log('Am an A'); }
15  
16  /**
17   * @constructor
18   * @implements {Sayer}
19   **/
20  function B() {}
21  B.say = function() { console.log('B factory'); };
22  B.prototype.say = function() { console.log('Am an B'); };
23  
24  /**
25   * @param {function(new:Sayer)} cls
26   **/
27  function makeSayer(cls) {
28      var obj = new cls();
29      cls.say();
30      obj.say();
31  }
32  
33  makeSayer(A);
34  makeSayer(B);

I can't understand how to get the google closure compiler for javascript to properly recognize properties of a constructor. For example, in the code below, I don't know what type to give 'cls' in line 27 so that the compiler does NOT produce the following (erroneous in my opinion) error:

foo.js:29: WARNING - Property say never defined on cls
cls.say();
^

From reading the documentation it seems that I should be doing it as I do in 25. Can someone help please? Thanks,

 1  /**
 2   * @interface
 3   **/
 4  function Sayer() {}
 5  Sayer.prototype.say = function() {};
 6  Sayer.say = function() {};
 7  
 8  /**
 9   * @constructor
10   * @implements {Sayer}
11   **/
12  function A() {}
13  A.say = function() { console.log('A factory'); };
14  A.prototype.say = function() { console.log('Am an A'); }
15  
16  /**
17   * @constructor
18   * @implements {Sayer}
19   **/
20  function B() {}
21  B.say = function() { console.log('B factory'); };
22  B.prototype.say = function() { console.log('Am an B'); };
23  
24  /**
25   * @param {function(new:Sayer)} cls
26   **/
27  function makeSayer(cls) {
28      var obj = new cls();
29      cls.say();
30      obj.say();
31  }
32  
33  makeSayer(A);
34  makeSayer(B);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心病无药医 2024-12-12 01:53:33

问题在于接口上定义的方法 (A.say) 与接口原型上定义的方法 (A.prototype.say) 之间的区别。在 Closure 的类型系统中,@interface< /code> 标签提供以下保证:

任何实现接口的类都必须实现所有接口
接口原型上定义的方法和属性。

请注意,它只关心原型上的方法。它不关心接口本身的方法。

假设您创建一个新类 C,它实现 Sayer。闭包将确保您定义了C.prototype.say。不过您可以跳过C.say。您不需要实施它。以下是完全有效的 Sayer

/**
 * @constructor
 * @implements {Sayer}
 */
function C() {}
C.prototype.say = function() { console.log('Am a C'); }

由于这是一个有效的 Sayer,因此我们应该可以安全地调用 makeSayer(C)。现在很清楚为什么您的 makeSayer 定义不正确。它假定已定义 cls.say,但 Closure 不提供此类保证。关闭会发现问题并给出适当的错误消息。

那么你应该做什么呢?

如果您想遵循标准的面向对象方法,那么您可能需要两个接口,一个用于工厂 SayerFactory,另一个用于它创建的 Say 对象。您的所有方法都将成为原型中定义的方法,并且 Closure 将为您完成所有检查。

但是,如果您喜欢在构造函数上使用 say 方法的想法,那么您可以尝试使用鸭子类型来强制工厂方法的存在。例如:

/**
 * @param {{say:function():Sayer}} cls
 */
function makeSayer(cls) {
  ...
}

当然,如果您需要定义多个方法,鸭子类型方法的扩展性不会很好。对于定义的每个方法,类型签名会变得越来越大。

祝你好运!

The issue is the distinction between a method defined on a interface (A.say) vs a method defined on an interface's prototype (A.prototype.say). In Closure's type system the @interface tag provides the following guarantee:

Any class that implements an interface must implement all of the
methods and properties defined on the interface's prototype.

Notice it only cares about methods on the prototype. It doesn't care about methods on the interface itself.

Suppose you make a new class, C, that implements Sayer. Closure will make sure you define C.prototype.say. However you can skip C.say. You are not required to implement it. The following is a perfectly valid Sayer.

/**
 * @constructor
 * @implements {Sayer}
 */
function C() {}
C.prototype.say = function() { console.log('Am a C'); }

Since this is a valid Sayer, we should be safe to call makeSayer(C). Now it is clear why your definition of makeSayer is incorrect. It assumes that cls.say is defined, but Closure makes no such guarantee. Closure spots the problem and gives you the appropriate error message.

So what should you do?

If you want to follow a standard object-oriented approach then you probably need two interfaces, one for the factory SayerFactory and one for the objects it creates Say. All of your methods will become methods defined in prototypes and Closure will do all your checking for you.

But if you like the idea of having a say method on your constructors then you could try using duck typing to enforce the presence of the factory method. Something like:

/**
 * @param {{say:function():Sayer}} cls
 */
function makeSayer(cls) {
  ...
}

Of course the duck typing approach won't scale very well if you need to define more than one method. The type signature would grow larger and larger for each method defined.

Good luck!

〃温暖了心ぐ 2024-12-12 01:53:33

接口上的“Sayer.say”将被忽略。

您是否正在尝试为 Closure 的高级模式做准备?在高级模式中,“A.say”和“B.say”被重命名为“A$say”和“B$say”,类型检查期望这一点并适当地抱怨它(不会有方法 A.say )。

您可以通过间接添加它来避免崩溃:

function addSay(sayer, fn) {
  sayer.say = fn;
}

我想您可以通过添加如下行来避免类型警告:

Function.prototype.say;

但我不确定这是否是您想要的。

您可以避免麻烦并将其添加到原型中并调用它:

A.prototype.classSay = function () {};  

"Sayer.say" on the interfaces is ignored.

Are you trying to prepare this for Closure's advanced mode? In advanced mode "A.say" and "B.say" are renamed to "A$say" and "B$say", the type check expects this and complains about it appropriately (there won't be a method A.say).

You can avoid collapsing by have it added by it indirectly:

function addSay(sayer, fn) {
  sayer.say = fn;
}

I suppose you could avoid the type warning by adding a line like:

Function.prototype.say;

But I'm not sure you this is what you want.

You can avoid the hassle and add it to the prototype and call that instead:

A.prototype.classSay = function () {};  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文