📝

【JavaScript】styled.a``とかgql``など、バッククオートで囲むあの構文(タグ付きテンプレート)について

2021/04/10に公開3

styled-componentsやgraphql-tagのライブラリを使用していると遭遇する、styled.a``とかgql``という`(バッククオート)で囲む構文について説明していきます。
https://github.com/styled-components/styled-components
https://github.com/apollographql/graphql-tag

styled-componentsを導入しようとしたある日のこと

Reactのスタイリングにはstyled-componentsがいいということで、早速自分のプロジェクトに導入してみようと思い、下記のサイトを開いてみました。
https://styled-components.com/

記載のソースコードが下記のとおりです。

const Button = styled.a`
  /* This renders the buttons above... Edit me! */
  display: inline-block;
  border-radius: 3px;
  padding: 0.5rem 0;
  margin: 0.5rem 1rem;
  width: 11rem;
  background: transparent;
  color: white;
  border: 2px solid white;

  /* The GitHub button is a primary button
   * edit this to target it specifically! */
  ${props => props.primary && css`
    background: white;
    color: black;
  `}
`

なんだこれは、、、、、よく分からん。
いや、何となく``で囲まれたところがCSSになっていて、そのスタイルがaタグに当てられたコンポーネントをButtonとして定義しているのは分かるのですが、いかんせん文法的になぜこれが成り立つのかはよく分かりません。

どうやら、タグ付きテンプレートと呼ぶ構文らしい

よく分からんが、実装はできるのでそのまま放置していたのですが、流石に気持ち悪く、調べたところ、タグ付きテンプレートと呼ばれる構文だというところに行き着きました。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals#tagged_templates

同じような``で囲む、下記のようなテンプレートリテラルの応用版のような位置づけのようです。

let a = 5;
let b = 10;
console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`);

タグ付きテンプレートのタグ(styled.aやgqlなど)は下記のように関数で定義します。

let person = 'Mike';
let age = 28;

function myTag(strings, personExp, ageExp) {
  let str0 = strings[0]; // "That "
  let str1 = strings[1]; // " is a "

  // There is technically a string after
  // the final expression (in our example),
  // but it is empty (""), so disregard.
  // let str2 = strings[2];

  let ageStr;
  if (ageExp > 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

  // テンプレートリテラルを用いて組み立てた文字列を返すこともできます
  return `${str0}${personExp}${str1}${ageStr}`;
}

let output = myTag`That ${ person } is a ${ age }`;

console.log(output);
// That Mike is a youngster

タグの関数の定義は下記の通り

// stringsにはシンタックスシュガー(${}のこと)で区切られた文字列が配列として入る
// keysにはシンタックスシュガーの値が入る
function template(strings, ...keys) {
	return ~~~~~~~~
}
console.log(template`${0}${1}${0}!`)
// console.log(template(["","","!"],0,"foo"))と同じ意味

いつ使うの、これ

自分の認識では基本的にアプリケーションレベルでタグを定義することはないと思っています。というより、あまり使用しないのがベターです。
関数の定義が直感的ではないため、不具合が生じた際、他の実装メンバーが中身を理解できないためです。

あくまで、今回紹介したstyled-componentsやgraphql-tagのようなstringをちょっと特殊な形で扱いたいライブラリを開発するときに使用されるものだと考えています。

アプリケーションの実装者としては、タグ付きテンプレートを扱ったライブラリを触る際には、このような構文が裏側で存在することは理解しておきつつ、READMEに素直に従ってタグを使用するのがいいのかなと思います。

Discussion

standard softwarestandard software

タグ付きテンプレートリテラルは奇妙キテレツな構文ですよね。

Jestで出てきたときに、なんでこれで動くの?使いづらい読みづらい構文だなと思いました。
私も使わないようにしています。

https://jestjs.io/ja/docs/api#2.-describeeachtablename-fn-timeout

toshiokuntoshiokun

コメントありがとうございます!(返信遅れてしまいすみません、、、)

奇妙キテレツだと共感していただきうれしいです、当たり前のように使われているので、最初この構文に違和感があるの自分だけかと思いましたw

standard softwarestandard software

すごい特殊なことしたいときにしか使ってはいけないかな、と思っています。
Jestの例だと、この構文書式で書かなくても、冗長だけど普通にまともに書く代替方法があるので、そっちでコード書いた方がわかりやすいだろうなと思っていました。