为什么闭包编译器不能识别自执行匿名函数内的类型声明?
通过闭包编译器运行相当大的库时,我收到很多“未知类型”警告,并且当我的类型在自执行匿名函数中声明时,它们似乎会发生。这没有什么奇怪的,但是如果我去掉自执行函数,类型声明似乎可以工作(至少在这个简单的测试中)。
我不确定我的代码注释是否有问题,或者代码中是否有任何非法内容,但我认为这都是合规的,也是模块化 API 的标准方法。
以下测试代码创建一个命名空间(只是一个普通的旧 JS 对象)并向其附加一个枚举(对象文字)和一个函数。
var mynamespace = {};
(function (mynamespace) {
/**
* Some enum.
* @enum {number}
*/
mynamespace.SomeEnum = {
FOO: 1,
BAR: 2
};
/**
* Frazzle some type.
* @param {mynamespace.SomeEnum} qux The type to frazzle.
* @return {boolean} whether the operation succeeded.
*/
mynamespace.frazzle = function(qux) {
return true;
}
}(mynamespace));
// call it
mynamespace.frazzle(mynamespace.SomeEnum.FOO);
看起来不错,对吧?闭包编译器错误:
[jscomp] Compiling 1 file(s) with 37 extern(s)
[jscomp] X:\dev\solclientjs\sdk\tools\jscomptest.js:14: WARNING - Parse error. Unknown type mynamespace.SomeEnum
[jscomp] * @param {mynamespace.SomeEnum} qux The type to frazzle.
I'm getting a lot of "Unknown type"
warnings when running a fairly large library through Closure Compiler, and they seem to occur when my types are declared in self-executing anonymous functions. There's nothing exotic about this, but if I strip the self-executing functions out, the type declarations seem to work (at least in this simple test).
I'm not sure if there's something wrong with my code annotations or if there's anything illegal in the code, but I think this is all kosher and the standard way to modularize an API.
The following test code creates a namespace (just a plain old JS object) and attaches an enum (an object literal) and a function to it.
var mynamespace = {};
(function (mynamespace) {
/**
* Some enum.
* @enum {number}
*/
mynamespace.SomeEnum = {
FOO: 1,
BAR: 2
};
/**
* Frazzle some type.
* @param {mynamespace.SomeEnum} qux The type to frazzle.
* @return {boolean} whether the operation succeeded.
*/
mynamespace.frazzle = function(qux) {
return true;
}
}(mynamespace));
// call it
mynamespace.frazzle(mynamespace.SomeEnum.FOO);
Looks fine, right? The closure compiler errors:
[jscomp] Compiling 1 file(s) with 37 extern(s)
[jscomp] X:\dev\solclientjs\sdk\tools\jscomptest.js:14: WARNING - Parse error. Unknown type mynamespace.SomeEnum
[jscomp] * @param {mynamespace.SomeEnum} qux The type to frazzle.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
编辑:
原来的答案完全不对。
这绝对是编译器中的一个错误。我还没有找到关于这个确切问题的错误报告,但我发现了两个错误报告似乎解决了这个问题的相反问题(编译器应该抛出警告,但除非您解开匿名函数,否则它不会)。
http://code.google.com/p/closure-compiler /issues/detail?id=134
http:// /code.google.com/p/closure-compiler/issues/detail?id=61
无论如何,匿名函数在与类型表达式一起使用时似乎都不太可靠。
Edit:
Original answer was totally off.
This definitely appears to be a bug in the compiler. I haven't found a bug report with this exact issue, but I found two bug reports that appear to address the inverse of this issue (compiler should be throwing a warning, but it won't unless you unwrap the anonymous function).
http://code.google.com/p/closure-compiler/issues/detail?id=134
http://code.google.com/p/closure-compiler/issues/detail?id=61
In any case it looks like anonymous functions are wonky when used with type expressions.
在您的示例中,参数“mynamespace”被传递给全局 mynamespace 对象。为全局对象 (global mynamespace) 设置别名 (local mynamespace) 会阻止对全局对象下的整个树进行优化。对于高级模式下的闭包编译器来说,这是一个糟糕的想法。
任何在局部变量下定义的函数都是易失性的。编译器不知道(没有深入的代码流分析)本地“mynamespace”是全局“mynamespace”的别名。因此,它不会自动将您在局部变量下定义的任何内容与其别名的对象关联起来。
执行此操作的正确方法是:
选项#1,直接使用全局对象:
选项#2,使用 goog.scope 别名(假设您使用的是高级模式):
这两个选项都应该为您提供所需的结果。但是,目前没有方法可以通过包装闭包中的参数来完成您想要的操作。
In your example, the argument "mynamespace" is being passed an "alias" to the global mynamespace object. Having an alias (local mynamespace) to your global object (global mynamespace) prevents optimizations on the entire tree under the global object. This is a BAD BAD idea for the Closure Compiler in Advanced mode.
Any function defined under a local variable is volatile. The compiler has no idea (without deep code flow analysis) the local "mynamespace" is an alias to the global "mynamespace". Therefore, it won't automatically associate anything you define under the local variable to the object that it aliases.
The proper ways to do this are:
Option #1, using the global object directly:
Option #2, use a goog.scope alias (assuming you are using Advanced mode):
Both options should get you your desired result. However, there is currently no way to do what you want via an argument into a wrapper closure.
闭包编译器在这里得到了改进。要更正上面的代码以使其被识别,您唯一需要做的就是将命名空间声明为 @const:
/** @const */
var mynamespace = {};
然后类型被识别。
The Closure Compiler has improved here. The only thing that you need to do to correct the code above so that it is recognized is to declare the namespace as @const:
/** @const */
var mynamespace = {};
The type is then recognized.
闭包编译器不支持本地类型名称。这些名称必须是全局名称,并且在函数“mynamespace”中是本地名称。
The Closure Compiler doesn't support local type names. The names must be global and within the function "mynamespace" is a local name.