おい、調子はどうだい?
最近、後輩のコードレビューをしていて「おっ、なかなかやるじゃん」と感心させられることがあったんだ。それが今回話す 「タグ付きテンプレートリテラル(Tagged Template Literals)」 を使ったコードだった。
普通のテンプレートリテラル(“ `Hello ${name}` “みたいなやつ)は、日々の開発で骨の髄まで染み込んでいると思う。でも、そのバックティックの直前に「関数名」を置くだけで、JavaScriptの表現力が一段階、いや二段階も跳ね上がるって知ってたかい?
今回は、公式ドキュメントの薄っぺらい解説じゃ物足りない、中級から一歩抜け出したいキミへ向けて、現場の泥臭いユースケースを交えながらこの機能の裏側と実践的な使い方を徹底的に叩き込んでやろう。
—
1. タグ付きテンプレートリテラルとは何か?(仕様の裏側)
まずは基本のおさらいと、ブラウザの心臓部で何が起きているのかの解剖からだ。
タグ付きテンプレートリテラルは、一言で言えば 「テンプレートリテラルをパースして組み立てる処理を、自前の関数でフックする仕組み」 だ。
百聞は一見に如かず。まずは一番シンプルなコードを見てくれ。
// タグ関数を定義
function myTag(strings, …values) {
console.log(‘Strings:’, strings);
console.log(‘Values:’, values);
return “加工された文字列”;
}
const name = “レジance”;
const role = “フロントエンド・アーキテクト”;
// バックティックの直前に関数を置く
const result = myTag`こんにちは、${name}さん。あなたは${role}です。`;
console.log(result);
これを実行したとき、ブラウザのJavaScriptエンジン(V8など)の裏側では何が起きていると思う?
ブラウザの裏側の処理
1. 静的な文字列の分割: エンジンはテンプレートを ${} で切り刻み、静的な文字列の配列(`strings`)を作る。今回の例なら `[“こんにちは、”, “さん。あなたは”, “です。”]` だ。
2. 特殊なプロパティ `raw`: 実はこの `strings` 配列には、エスケープシーケンス(`\n` など)をそのまま保持した `raw` プロパティが生えている。これが後々めちゃくちゃ効いてくる。
3. 式の評価: `${name}` や `${role}` の部分が評価され、残りの引数(Restパラメータ `…values`)としてガッサーと渡される。
つまり、タグ関数というのは「文字列のパーツ」と「変数のパーツ」をバラバラの状態で受け取り、料理し直して返すシェフのようなものなんだ。
—
2. なぜ実務で使うのか?(現場のユースケース)
「へえ、面白い仕組みだね。で、いつ使うの?」って思ったかい?
正直、日々の適当な文字列結合なら普通のテンプレートリテラルで十分だ。だが、以下のシチュエーションにぶぶ当たったとき、タグ付きテンプレートリテラルは唯一無二の救世主になる。
1. XSS(クロスサイトスクリプティング)対策の自動化
2. 多言語化(i18n)のスマートなインライン化
3. CSS-in-JS や SQLクエリビルダーの自作
今回は、この中でも実務で一番遭遇しやすい 「HTMLの自動エスケープ(XSS対策)」 をテーマに、そのままコピペしてプロダクトで使えるコードを作ってみた。
—
3. 実践!安全なHTMLを自動構築するタグ関数
フロントエンド開発で一番やっちゃいけないミス、それは「ユーザーからの入力をそのままHTMLに埋め込んでXSSを踏み抜くこと」だ。いちいち `escapeHTML()` 関数を通すのは、人間である以上絶対に忘れる。
だったら、テンプレートリテラル自体にサニタイズを義務付ければいい。
/
- 与えられた文字列のHTML特殊文字をエスケープするヘルパー関数
/
function escapeSpecialChars(str) {
return String(str)
.replace(/&/g, ‘&’)
.replace(//g, ‘>’)
.replace(/”/g, ‘"’)
.replace(/’/g, ‘'’);
}
/
- XSSを防ぐための安全なタグ付きテンプレートリテラル
- @param {TemplateStringsArray} strings 静的文字列の配列
- @param {…any} values 埋め込まれる変数群
/
function html(strings, …values) {
// reduceを使って、静的文字列とエスケープされた変数を交互に結合していく
return strings.reduce((accumulator, currentString, index) => {
const correspondingValue = values[index – 1];
// 変数が存在する場合は、安全のためにエスケープ処理を強制する
const safeValue = correspondingValue !== undefined
? escapeSpecialChars(correspondingValue)
: ”;
return accumulator + safeValue + currentString;
});
}
// — 実際の利用シーン —
const userInput = ‘‘;
const userName = “山田 太郎”;
// htmlタグをつけるだけで、userInputに含まれる危険なタグが無害化される
const safeOutput = html`
名前: ${userName}
自己紹介: ${userInput}
`;
console.log(safeOutput);
// 出力結果(ブラウザのコンソールなどで確認してみてくれ):
//
名前: 山田 太郎
//
自己紹介: <script>alert("XSSアタック!");</script>
//
どうだ? これなら、開発者がうっかりエスケープを忘れても、タグ関数 `html“ が強制的に悪意あるスクリプトを無害な文字列に変換してくれる。チーム開発におけるヒューマンエラーを仕組みで防ぐ、これぞシニアエンジニアの技だ。
—
4. 知っておくべき注意点と罠
もちろん、万能な技術なんて存在しない。タグ付きテンプレートリテラルを使う上での「お作法」というか、ハマりどころも共有しておこう。
- パフォーマンスへの過度な期待は禁物
文字列操作を挟む分、通常のテンプレートリテラルよりもわずかにオーバーヘッドがある。とはいえ、通常のUI構築でボトルネックになることは稀だ。ただし、超高頻度で呼ばれるループ内での乱用は避けること。
- TypeScriptとの相性
もしTypeScriptを使っているなら、タグ関数の第一引数の型は `TemplateStringsArray` になる。型推論が賢いので難しくはないが、戻り値や可変長引数(`…values: any[]`)の型安全性をどこまで担保するかは設計が必要になる。
—
5. まとめ
タグ付きテンプレートリテラルは、ただの「おしゃれな構文糖衣」じゃない。
JavaScriptのパーース処理に直接介入し、ドメイン特化型の言語(DSL)を作り出すための強力な武器だ。
styled-components や GraphQL の gql タグなども、内部でやっていることは今回解説した仕組みの親戚みたいなもの。この仕組みを理解していれば、ライブラリの裏側で何が行われているのかが手に取るようにわかるはずだ。
さて、理論はここまでだ。
今日の業務で書くコードのどこかに、この `html“ タグをこっそり(いや、ドヤ顔で)導入してみてくれ。キミの書くコードの信頼性が、また一段階引き締まるはずだからさ。
それじゃ、次のコードレビューでまた会おう。ハッピー・コーディング!

コメント