文字列定数は便利だ。
const CONST_STR = "str";
- typoが防げる
- 補完の効くエディタでタイプ量が減る
- リネームなどのリファクタリングが容易になる
問題は数が増えてきた時。
const UNIQUE_NAME_STR1 = "...";
const UNIQUE_NAME_STR2 = "...";
const UNIQUE_NAME_STR3 = "...";
const UNIQUE_NAME_STR4 = "...";
const UNIQUE_NAME_STR5 = "...";
//...
const UNIQUE_NAME_STR100 = "...";
同じスコープで全部定義すると、定数名が長くなってしまう。
またエディタの補完が効くといっても、今度は候補数が多すぎて選ぶのが大変だ。
そこで分類毎にスコープを分けて管理・定義することにする。
分類.CONST
のようにしたい。TypeScriptではどうするのが良いか。
× class
さいしょはclass
を使おうと考えたが、class
の プロパティとして文字列定数は定義できない 1。
class myConst{
static const STR1 = "..."; //error!
}
こうすればできる。が、これは 変数 だ。書き換えられては困る。
class myConst{
static STR1 = "..."; //書き換え可能
}
書き換えられなければいいんだな?ということでgetter
を定義してみるが、あまりにも冗長だ。関数呼び出しのパフォーマンス低下も気になる。
class myConst{
static get STR1(){return "...";}
}
というわけで機能的には使えるが、採用しなかった。
× interface
TypeScriptのinterface
は他の言語に比べると機能が多いのでできないかなと思ったが、そもそも定義の実体を書けないのでムリ。
× object
class
と同じくgetterプロパティを定義すればいいが、めちゃくちゃ冗長になるのでナシ。
var myConst = {};
Object.defineProperty(myConst, "STR1", {
get: () => "...",
enumerable: true,
configurable: true
});
※追記:get
キーワード使えた。冗長なのはclassと同じ。
var myConst = {
static get STR1(){return "...";}
};
○ 内部モジュール(module) / namespace
試行錯誤の上採用した方法は内部モジュール(module
)を使う方法だった。外部から使うのでexport
とだけつける必要があるが、冗長な書き方も必要なくスッキリかける。
module myConst{
export const STR = "...";
}
そもそも同一スコープで、が問題の原因だったので、スコープをわけるための機能を使えばいい、という分かれば何ともない話だった。
分類毎に分けてみる
TypeScriptはver.1.5からnamespace
という機能が使える。単に内部モジュールと同じものなのだが、別物の内部モジュール・外部モジュールに同じ言葉で説明する混乱がなくなるので、まさにやりたい事の名前をしているnamespace
を使ってみる。
簡単なライブラリっぽいものを作ったと仮定してみるとこんな感じ。
/** ライブラリの文字列定数 */
namespace myConst{
export const NAME = "myLibrary";
export const LICENSE = "MIT";
export const VERSION = "1.0.1";
export const PATH = "http://example.jp/path/to/"
//...
}
/** Event種別の文字列定数 */
namespace myConst.Event{
export const LOAD = "load";
export const AWAKE = "awake";
export const CLICK = "click";
//...
}
/** ロゴ関係の文字列定数 */
namespace myConst.Logo{
export const PATH = "path/to/image";
export const ALT = "myLibrary logo.";
//...
}
console.log(myConst.Logo.ALT); //"myLibrary logo."
- 構造化されて奇麗で見やすい
- 定数の文字数が最小限
-
LIB_URL_PATH
とLOGO_PATH
に分けなくてもいい - 定数だけ管理しているので無駄な補完候補がでない
namespace
(内部モジュールも)は入れ子ができる。これをうまく使って奇麗に管理できる2。分かりやすくて使いやすくて安心だ。
補足:深い階層を作ってしまったとき
調子に乗って分類しすぎてしまった場合や、名前空間のバッティングを避けて深い階層のものを作った場合。
namespace myConst.long.long.long.very.very.very.long.path{
export const STR = "...";
}
//usage
myConst.long.long.long.very.very.very.long.path.STR;
いくら補完が効いてもこの長さは厳しい。
そんなときは変数や定数に代入してしまうといい。
const m = myConst.long.long.long.very.very.very.long.path;
console.log(m.STR); //"..."
import
文でもほぼ同じ。
import m2 = myConst.long.long.long.very.very.very.long.path;
console.log(m2.STR); //"..."
意味的にはimport
の方がいいかも?