やりたかったこと
Enum を定義しているような場合に、共通メッセージ用のテンプレート文字列を定義しておいて、必要な時に placeholder に対して任意の値を入れて使いたい。
例えば Python だと、string.format()
を使うことで下記のようにできます。
message = "[Error] code: {code}, message: {message}"
print(message.format(code="000", message="error message text"))
# >>> [Error] code: 000, message: error message text
JavaScript ではこれに該当するメソッドがないので、独自の実装を作って使っている人も多いみたいです (ショシンシャー なのでよくわからん)。皆大好き stackoverflow では Yahoo (.com) のライブラリの例として下記のようなコードが貼られてました。
YAHOO.Tools.printf = function() {
var num = arguments.length;
var oStr = arguments[0];
for (var i = 1; i < num; i++) {
var pattern = "\\{" + (i-1) + "\\}";
var re = new RegExp(pattern, "g");
oStr = oStr.replace(re, arguments[i]);
}
return oStr;
}
// YAHOO.Tools.printf('{0} {1} {2}', 'aaa', 'bbb', 'ccc');
template literals 使う
もうちょっと手軽でスマートな感じにしたい、そこで思いつくのは
ES6 で入ってきたおなじみの template literals ですが、
const templateString = `[Error] code: ${code}, message: ${message}`;
上記の場合の問題点は、事前に code
/ message
変数が定義されている必要があることです。
そこで、文字列を下記のように関数で囲ってしまいます。
const templateString = ({code, message}) => `[Error] code: ${code}, message: ${message}`;
また上記の例では分割代入を使っているので、呼び出し側では引数の順番を考慮する必要がなく、キーに対して値を指定するのでコードの可読性も高いと思います。
const templateString = ({code, message}) => `[Error] code: ${code}, message: ${message}`;
// どちらも出力は同じ
console.log(templateString({code: '000', message: 'error message text'}));
console.log(templateString({message: 'error message text', code: '000'}));
// >>> [Error] code: 000, message: error message text
これなら楽ですね!
番外編
実は上記の方法を知るまでは、Tagged template literals を使って同様のことをやろうとしていました。仕様はドキュメントを見てもらえればと思いますが、下記のようになります。
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
// コードは上記からまんまなので、実際にはテンプレート文字列中で、
// `${0}` のように、整数 or `${'keyword'}` 形式で指定可能
function template(strings, ...keys) {
return (function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
});
}
const templateString = template`[Error] code: ${'code'}, message: ${'message'}`;
console.log(templateString({code: '000', message: 'error message text'}));
// >>> [Error] code: 000, message: error message text
関数を定義しておき、テンプレート文字列を定義する時にこの関数を"タグ付け"しておくことで、呼び出し時に任意の処理を加えることができます。今回の例では単純に文字列の結合処理のみでしたが、Tagged template literals は他にも色々と応用が効きそうです。