Как получить закрывающий компилятор для правильной проверки свойств конструктора в JavaScript - PullRequest
1 голос
/ 18 сентября 2011

Я не могу понять, как получить компилятор Google Closure для JavaScript, чтобы правильно распознать свойства конструктора. Например, в приведенном ниже коде я не знаю, какой тип дать 'cls' в строке 27, чтобы компилятор НЕ выдавал следующую (на мой взгляд, ошибочную) ошибку:

foo.js: 29: ПРЕДУПРЕЖДЕНИЕ. Свойство 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);

Ответы [ 2 ]

0 голосов
/ 10 октября 2011

Проблема заключается в различии метода, определенного в интерфейсе (A.say), от метода, определенного в прототипе интерфейса (A.prototype.say).В системе типов Closure тег @interface предоставляет следующую гарантию:

Любой класс, реализующий интерфейс, должен реализовывать все методы и свойства, определенные в прототипе интерфейса.

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

Предположим, вы создаете новый класс 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) {
  ...
}

Конечно, подход по типу утки не очень хорошо масштабируется, если вам нужно определить более одного метода.Сигнатура типа будет увеличиваться для каждого определенного метода.

Удачи!

0 голосов
/ 20 сентября 2011

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