はじめに
JavaScript 大好きな私が、JavaScript の文字列について熱く語るブログです。
正月休み中に執筆したので、趣味全開のブログをお届けします。
JavaScript の文字列というと、"String"
や 'String'
は広く知られています。
しかし、`String`
と表記する Template Literal (テンプレート文字列) はその真価が伝わりきっていないかなと考えます。
Template Literal を使うことで、コードの可読性だけでなく保守性も向上します。
また、Template Literal は、SQL インジェクションなどのセキュリティリスクを軽減するのにも役立ちます。
このブログでは、Template Literal について熱く語りたいと思います。
(メインで使う言語が TypeScript のため、例には一部 TypeScript を含みます)
Template Literal を熱く語る
基本的な用法
Template Literal の最も基本的な使い方で言えば、テンプレートの名前通り値を展開できます。
const a = 1 const b = 2 console.log(`${a} + ${b} = ${a + b}`) // 1 + 2 = 3
いろいろな言語の format
のように、${}
で指定された範囲を展開できます。
これは、変数の値を組み込む用途に於いて可読性が高まる意味で有用な使い方です。
また、複数行入力に対応している点も、Template Literal の特徴です。
const lines1 = "line1\nline2\nline3\n" const lines2 = `line1 line2 line3 ` console.log(lines1 === lines2) // true
このように、複数行を直感的に記載することができます。
このように、変数展開を行わないような、通常の文字列入力として考えた場合であっても、Template Literal は通常の文字列型と比較して可読性で有意です。
が、こんなことが書きたいわけではなく次からが本質です。
SQL Injection と Template Literal
SQL Injection という、username
などに SQL として悪意ある構文を挿入し、データベースから情報の詐取や破壊を行う攻撃があります。
const username = "' OR '1'='1'; --" const sql = "SELECT * FROM users WHERE username = '" + username + "';" console.log(sql) //SELECT * FROM users WHERE username = '' OR '1'='1'; --';
この例のように、SQL を文字列として組み立てることは絶対にしてはいけないと強く言われています。
では、これを Template Literal として実装するとどうでしょうか?
const username = "' OR '1'='1'; --" const sql = `SELECT * FROM users WHERE username = '${username}';` console.log(sql) //SELECT * FROM users WHERE username = '' OR '1'='1'; --';
当たり前ですが、結果は変わりません。
しかし、私は思います。
この表記は分かりやすいのに、なぜ Statement
を使わないといけないのかと。
こういったユースケースに対して、実は強力に機能するのが Template Literal です。
タグ付きテンプレート
Template Literal には、タグ付きテンプレート という機能があります。
先ほどの例に対して、少し魔法をかけてみます。
const sqlTag = (strings: TemplateStringsArray, ...args: any[]): [string, ...any[]] => { const sql = strings.join(" ? ") return [sql, ...args] } const username = "' OR '1'='1'; --" const sql = sqlTag`SELECT * FROM users WHERE username = ${username};` console.log(sql) // 0: "SELECT * FROM users WHERE username = ? ;" // 1: "' OR '1'='1'; --"
ハイ、天才〜〜〜!!!
const sql = sqlTag`SELECT * FROM users WHERE username = ${username};`
大切なところはここです。
なんと、天才的なことに SQL 内に変数を直接入れたように書いたのに、SQL は Statement を使う形で安全に展開されています。
Statement で失われた可読性が、完全に蘇っています。
Template Literal は、実はタグという形で関数を読み出すことができるのです。
今回は、単純に文字列を結合するだけでしたが、実は使い方は無限大です。
すごく大切なことなのですが。
Template Literal は文字列型から『任意の戻り値』を返すことができます。
戻り値が、文字列である必要すらありません。
やりたい放題、なんでもできます。
タグ付きテンプレートの作成
詳細は MDN などの信頼できる情報源をみてもらうとして、Template Literal の作成について簡単に触れたいと思います。
const myTag = (strings: TemplateStringsArray, ...args: any[]) => { /// your code }
以上です。
雑かよ、と言われそうですが、結構本当に『以上』だったりします。
strings には Template Literal 内の ${}
を除く文字列が順に配列で入っています。
TypeScript の型指定は TemplateStringsArray
が便利です。
args には、 ${}
内の変数が順次入っています。
煮たり焼いたり、自由に扱うことができます。
タグ付きテンプレートの戻り値は、関数の戻り値になります。
大好きなライブラリの紹介
ここまで来ると、じゃあどんな使い方をしているのか? という話になります。
有用なライブラリをいくつか紹介します。
styled-components
styled components は、React のコンポーネントに対するスタイルライブラリです。
React で CSS を適用する場合、独自のオブジェクトを付与することが求められます。
<Button style={{backgroundColor: "#112233", color: "#ffeedd"}} />
可読性がなく、直感的ではなく、PR の際に指摘されること請け合いです。
これが、こうなります。
ぐぐって出てくる CSS を直接記入出来るようになります!
styled.Button` background-color: #112233; color: #ffeedd; `;
sql-template-strings
sql-template-strings は、SQL を安全に構成するためのライブラリです。
Template Literal を活用して、MySQL や PostgreSQL などの複数のデータベース向けの Statements を作成できます。
公式の例を見ただけでも、Placeholder を利用した例との可読性の差にうっとりします。
db.query(SQL` INSERT INTO books (name, author, isbn, category, recommended_age, pages, price) VALUES (${name}, ${author}, ${isbn}, ${category}, ${recommendedAge}, ${pages}, ${price}) `)
graphql-tag
graphql-tag は、GraphQL 用の変換ライブラリです。
公式の例にありますが、たったこれだけの指定から膨大な量の GraphQL AST を生成します。
とてもじゃないけど、人間のかける量では、、、という感じです。
const query = gql` { user(id: 5) { firstName lastName } } `
その他
と、言いたいところですが、実は Template Literal を活用した例は多くありません。
私が知っている例だと、styled component 系、言語変換系 (SQL、 GraphQL、 etc) くらいでしょうか?
皆さんの知っているライブラリがあったら、X(旧 Twitter) までご連絡ください!
まとめ
いかがでしたでしょうか?
Template Literal は、実はとってもすごいということがわかりましたが、その活用はこれからということもわかりました。
新しい情報が入りましたら、どんどん更新していきますので、お気に入り登録をして待っていてください!