Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

Closure Library でカタいクラスを実装する

ここではカタいクラス=フェイルファストで、コンパイル時に問題を発見しやすく、かつ保守しやすいクラス と定義する。
基本的には goog.ui.Component あたりを読むと勉強になる。もちろんネームスペースでユーティリティメソッド群を実装する場合にも通じる。

goog.asserts.assert を使う

「ここではこういう理由からこういう状態であるべき」をコードで明記できる。例えばgoog.ui.Componentでは以下のようにして使っている。

/**
 * Similar to {@code getElementByClass} except that it expects the
 * element to be present in the dom thus returning a required value. Otherwise,
 * will assert.
 * @param {string} className The name of the class to look for.
 * @return {!Element} The first item with the class name provided.
 */
goog.ui.Component.prototype.getRequiredElementByClass = function(className) {
  var el = this.getElementByClass(className);
  goog.asserts.assert(el, 'Expected element in component with class: %s',
      className);
  return el;
};

@param, @type, @return で ! や ? をきちんと書く

Javaの人としてはundefinedやnullになりえる場合は明記したい。

JSDocで@param {?Object} object description とすればnullになる可能性がある(nullを許容する)ことの明記になる。undefinedは{(Object|undefined)}とする。
逆に、nullになる可能性がない(nullを許容しない)場合は@param {!Object} object descriptionで良い。

@paramの場合は=を使って省略可能であることも明記できる。この場合、=の右側にデフォルト値を併記しておくとドキュメント生成に良い。

追記:はてぶコメントで指摘いただきました、stringはプリミティブなのでnullにはならないそうです。

@override の代わりに @inheritDoc を使えないか検討する

ドキュメントで説明すべき事柄がスーパークラスと同じ場合、@override の代わりに @inheritDoc を使うことでスーパークラスのドキュメントを使える。
Closure Library自身にもけっこうドキュメントが空っぽなメソッドがあるが、このタグを使えば解決できることが多い。

追記:はてぶコメントで指摘いただきました、deprecatedになっているそうです。

@typedef とRecord TypeでPlain Objectの中身について型情報を明記する

Record Typeを使うと、Plain Objectの中身(プロパティ)について名前と型情報を明記できる。Plain Objectを使いたい、値を表すクラスを作るまでもないケースで有用。

同じPlain Objectを使うメソッド複数ある場合、JSDocをコピペするのではなく、@typedefで型を定義すると良い。例えば goog/fs/fs.js には2メソッドを持つPlain ObjectがUrlObject_として定義されている。

/**
 * @typedef {{createObjectURL: (function(!Blob): string),
 *            revokeObjectURL: function(string): void}}
 */
goog.fs.UrlObject_;

XxxLike を理解して使う

JavaScriptでよくある「ArrayみたいだけどArrayじゃないモノで、だけどArrayとして使える」オブジェクトをXxxLike と表すのがClosure Library流っぽい。Array等を扱うメソッドで代わりにこれらを使うようにすると、カタさを保ちつつ柔軟性を上げられる。

  • goog.array.ArrayLike
  • goog.date.DateLike
  • goog.events.EventLike
  • goog.net.XhrLike

ちなみに goog.dom.isNodeLike() というメソッドもあるが型として定義されているわけではない。

ツールをちゃんと使う

省略。別途資料等を参照。