タグ付きテンプレートリテラルで遊ぶ


## Tagged Template Litterals とは



簡単に説明すると以下がそれぞれ等価です。

```js
fn`this is a ${aVar} day`;
fn([ 'this is a ', ' day' ], aVar);
```

```js
fn`this is a ${aVar} day${'!'}`;
fn([ 'this is a ', ' day', '' ], aVar, '!');
```

詳細は以下を見てください。
MDNドキュメント: [タグ付けされたTemplate literal](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/template_strings#%E3%82%BF%E3%82%B0%E4%BB%98%E3%81%91%E3%81%95%E3%82%8C%E3%81%9FTemplate_literal)

---

正直使いどころは思い浮かばないです。
知っている限りだと [styled-components](https://www.styled-components.com/docs/advanced#tagged-template-literals) ではめっちゃ便利に感じてます。

どんなことが出来るのか考えてみたことを書いておきます。

## 関数の generator
styled-components と似たパターン

```js
function embedFuncBuilder(parts, ...funcs) {
  return params =>
    funcs.reduce((p, c, k) => p + c(params) + parts[k + 1], parts[0]);
}
```

使う時

```js
const githubURL = embedFuncBuilder`https://github.com/${p => p.name}`;

githubURL({ name: "elzup" });
// => https://github.com/elzup
```

```js
const explodeHeader = p =>
  Object.keys(p.headers)
    .map(k => `-H '${k}: ${p.headers[k]}'`)
    .join(" ");
const curlCommand = embedFuncBuilder`curl -X ${p => p.method} ${p => p.url} ${explodeHeader}`;

curlCommand({
  method: "GET",
  url: "https://elzup.com",
  headers: { HOGE: "v1", FUGA: "v2" }
});
// => curl -X GET https://elzup.com -H 'HOGE: v1' -H 'FUGA: v2'
```

リテラルの部分を変数として保持できないのでありがたみがない。

Flow版

```js
// @flow
'use strict'

type EmbedFunc<T> = T => string

function embedFuncBuilder<T>(
	parts: string[],
	...funcs: EmbedFunc<T>[]
): EmbedFunc<T> {
	return params =>
		funcs.reduce((p, c, k) => p + c(params) + parts[k + 1], parts[0])
}

type Args = { name: string }

const githubURL: EmbedFunc<Args> = embedFuncBuilder`https://github.com/${p =>
	p.name}`

githubURL({ name: 'elzup' })
// => https://github.com/elzup
```


![Fullscreen_2017_10_26_1_06.png](https://qiita-image-store.s3.amazonaws.com/0/40717/38c343b4-7871-4580-3bcc-acc8a241fee4.png)
引数の型付けは効かせられそうです。

## 埋め込みにフィルタ
途中に map をかませる

```js
function mapTemplate(callback) {
  return (parts, ...vals) =>
    vals.reduce((p, c, k) => p + callback(c) + parts[k + 1], parts[0]);
}
```

```js
const v = {
  a: 0,
  b: 200,
  c: 23
};

const fillTemplate = mapTemplate(v => ("00" + v).slice(-3));
const doubleTemplate = mapTemplate(v => v * 2);

console.log(`a: ${v.a}, b: ${v.b}, c: ${v.c}`)
// => a: 0, b: 200, c: 23
console.log(fillTemplate`a: ${v.a}, b: ${v.b}, c: ${v.c}`)
// => a: 000, b: 200, c: 023
console.log(doubleTemplate`a: ${v.a}, b: ${v.b}, c: ${v.c}`)
// => a: 0, b: 400, c: 46
console.log(mapTemplate(v => -v)`a: ${v.a}, b: ${v.b}, c: ${v.c}`)
// => a: 0, b: -200, c: -23

```


## PrintFormat 関数的なもの
Python2?に似てる

```js
function format(arr) {
  return (parts, ...vals) =>
    vals.reduce((p, c, k) => p + `${arr[c]}` + parts[k + 1], parts[0]);
}
```

```js
console.log(format([1, 2, 3])`${0} + ${1} - ${0} = ${2} - ${0}`)
// => 1 + 2 - 1 = 3 - 1
console.log(format(['nya-'])`(Ф∀Ф)${0}${0}`)
// => (Ф∀Ф)nya-nya-
```


## 拡張する際のUtil
ここまで書いてきて reduce で同じような処理を書いています。
Tagged Template Literals で生まれる引数の特殊なパターンのせいです。
前述の mapTemplate 関数っぽく Array.reduce の Wrap 関数を用意するとそれぞれ簡潔に書けそうです。

```js
function reduceJoin(callback) {
  return (parts, ...vals) => {
    return vals.reduce(
      (p, c, i, a) => p + callback(p, c, i, a) + parts[i + 1],
      parts[0]
    );
  };
}


// Flow type
function reduceJoin<T>(
	callback: (
		previousValue: string,
		currentValue: T,
		index: number,
		array: any[]
	) => string
) {
	return (parts: string[], ...vals: T[]) => {
		return vals.reduce(
			(p, c, i, a) => p + callback(p, c, i, a) + parts[i + 1],
			parts[0]
		)
	}
}

```

例えば "PrintFormat的なの" は以下の用に書けるようになりました。


```js
function format(arr) {
	return (parts, ...vals) =>
		vals.reduce((p, c, k) => p + `${arr[c]}` + parts[k + 1], parts[0])
}

// ↓

function format(arr: any[]) {
	return reduceJoin((_, v) => arr[v])
}

```

## おわりに

どのパターンもイマイチ使おうとは思えないです。
今後も思いついたらメモ感覚で追加しようと思ってます。