Help us understand the problem. What is going on with this article?

[JavaScript] template literals でなんちゃって string.format()

やりたかったこと

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 は他にも色々と応用が効きそうです。

参考: Tagged Template literals — Its more than you think

jrsyo
Engineer
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした