4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

テンプレートリテラルを押し付けて実行する関数

Posted at

テンプレートリテラル

テンプレートリテラルはJavaScriptにおける文字列の表現方法の1つです。バッククォート(`)を用いて`hello`のように式を記述します。
テンプレートリテラルは記述中に異なる式を埋め込めることが特徴的です。ドル$と波括弧{}を用いて式を埋め込みます。

const familyName = 'Einstein';
const givenName = 'Albert';

// Albert Einstein
const fullName = `${givenName} ${familyNmae}`

上記の例ではテンプレートリテラルを用いてfamilyNamegivenNameを埋め込んだ文字列fullNameを生成しています。

タグ付きテンプレート

テンプレートリテラルは稀に下記のように利用されることがあります。

tag`${givenName} ${familyNmae}`

tagという関数に対してテンプレートリテラルを押し付けるような形です。
Next.jsの利用例としてServer Actions内でSQLを呼ぶ例が執筆当時では話題となっていて、そこでも@vercel/postgressql関数でタグ付きテンプレートを利用しています(話題となっている理由は別のところですが)。

sql`SELECT * FROM posts WHERE likes > ${likes};`

これはテンプレートリテラルのより高度な形式でタグ付きテンプレートと呼ばれます。この形式におけるタグとはtagsqlのようなテンプレートリテラル前の関数を指しています。
関数では押し付けられたテンプレートリテラルについて詳細な情報を引数から扱えます。

const analysisTemplateLiterals = (
  strings,
  ...keys
) => {
  console.log(strings);
  console.log(keys);
};
// ['', ' ', '', raw: Array(3)]
// ['Albert', 'Einstein']
analysisTemplateLiterals`${givenName} ${familyNmae}`;

上記の関数で入力を確認すると、第1引数はテンプレートリテラルの埋め込み単位で分割した文字列が配列で渡されます(raw: Array(3)については後述)。第2引数以降は残余引数として埋め込んだ式が渡されます。

これらを利用してHTMLエスケープする便利な関数escapeHtmlを作成できます(ここで作成する関数が完全にHTMLエスケープすることは保証しません)。

const escapeHtml = (
  strings,
  ...keys
) => {
  const escapedValues = keys.map(key => {
    return String(key)
      .replace(/&/g, '&')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  });
  let result = '';
  for (let i = 0; i < strings.length; i++) {
    result += strings[i];
    if (i < escapedValues.length) {
      result += escapedValues[i];
    }
  }
  return result;
};

ユーザの入力を元にした値をこれを利用することによってescapeHtml`Hello ${userInput}!` 簡単にHTMLエスケープした文字列として扱えます。

話は戻ってタグが受け取る第1引数にあったraw: Array(3)について注目します。
以下のようにrawの部分と比較するようにタグを記述して動作を見ます。

const analysisRaw = (strings) => {
  console.log(strings);
  console.log(strings.raw);
};

analysisRaw`Good Morning\nGood Evening`
// ['Good Morning\nGood Evening', raw: Array(1)]
// ['Good Morning\\nGood Evening']

const analysisRawFirst = (strings) => {
  console.log(strings[0]);
  console.log(strings.raw[0]);
};

analysisRawFirst`Good Morning\nGood Evening`
// Good Morning
// Good Evening
// Good Morning\nGood Evening

consoleでは\nによる改行がされていない元の文字列が出力されました。rawはこのようにテンプレートリテラルが入力された時の文字列をそのまま持つ配列で、第一引数の配列に対応してそれぞれ格納されています。

このように第1引数はstringの配列以外にもrawを持つのでTypeScriptではTemplateStringsArrayとして実装されています。

interface TemplateStringsArray extends ReadonlyArray<string> {
  readonly raw: readonly string[];
}

さいごに

この記事ではタグ付きテンプレートについて紹介しました。知っていない時にこの表現を見ると理解し難い式ですが、知っていると綺麗にハマるケースも多いので活用できるようになりたいです。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?