Почему Closure Compiler не распознает объявления типов внутри самопроизвольной анонимной функции?

Я получаю много предупреждений "Unknown type" при запуске довольно большой библиотеки через Closure Compiler, и они, похоже, возникают, когда мои типы объявляются в самоисполняющихся анонимных функциях. В этом нет ничего экзотичного, но если я отключу самоисполняемые функции, объявления типа будут работать (по крайней мере, в этом простом тесте).

Я не уверен, что что-то не так с моими аннотациями кода или если в коде есть что-то незаконное, но я думаю, что это все кошерный и стандартный способ модулизации 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.

Ответы

Ответ 1

Edit:

Оригинальный ответ был полностью отключен.

Это определенно кажется ошибкой в ​​компиляторе. Я не нашел отчет об ошибке с этой точной проблемой, но я нашел два отчета об ошибках, которые, как представляется, обращаются к обратному решению этой проблемы (компилятор должен вызывать предупреждение, но это не произойдет, если вы не развернете анонимную функцию).

http://code.google.com/p/closure-compiler/issues/detail?id=134

http://code.google.com/p/closure-compiler/issues/detail?id=61

В любом случае, похоже, что анонимные функции являются неудобными при использовании с выражениями типов.

Ответ 2

В вашем примере аргументу "mynamespace" передается "псевдоним" для глобального объекта mynamespace. Наличие псевдонима (local mynamespace) для вашего глобального объекта (глобальное пространство имен) предотвращает оптимизацию всего дерева под глобальным объектом. Это идея BAD BAD для компилятора Closure в расширенном режиме.

Любая функция, определенная под локальной переменной, нестабильна. Компилятор понятия не имеет (без глубокого анализа потока кода) локальное "mynamespace" является псевдонимом глобального "mynamespace" . Поэтому он не будет автоматически связывать все, что вы определяете под локальной переменной, с объектом, который он псевдонизирует.

Правильные способы сделать это:

Вариант №1, используя глобальный объект напрямую:

(function() {
    mynamespace.someEnum = ...
})();

Вариант № 2, используйте псевдоним goog.scope(если вы используете расширенный режим):

goog.scope(function() {
    var somevar = mynamespace;

    (function() {
        somevar.someEnum = ...
    })();
});

Оба варианта должны дать вам желаемый результат. Однако в настоящее время существует нет способ сделать то, что вы хотите, с помощью аргумента в закрытие оболочки.

Ответ 3

Компилятор Closure улучшился. Единственное, что вам нужно сделать, чтобы исправить код выше, чтобы он был распознан, - объявить пространство имен как @const:

/** @const */ var mynamespace = {};

Затем распознается тип.

Ответ 4

Компилятор Closure не поддерживает имена локального типа. Имена должны быть глобальными и внутри функции "mynamespace" является локальное имя.