LoginSignup
1
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-10-25

Tagged Template Litterals とは

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

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

詳細は以下を見てください。
MDNドキュメント: タグ付けされたTemplate literal


正直使いどころは思い浮かばないです。
知っている限りだと styled-components ではめっちゃ便利に感じてます。

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

関数の generator

styled-components と似たパターン

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

使う時

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

githubURL({ name: "elzup" });
// => https://github.com/elzup
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版

// @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
引数の型付けは効かせられそうです。

埋め込みにフィルタ

途中に map をかませる

function mapTemplate(callback) {
  return (parts, ...vals) =>
    vals.reduce((p, c, k) => p + callback(c) + parts[k + 1], parts[0]);
}
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?に似てる

function format(arr) {
  return (parts, ...vals) =>
    vals.reduce((p, c, k) => p + `${arr[c]}` + parts[k + 1], parts[0]);
}
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 関数を用意するとそれぞれ簡潔に書けそうです。

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的なの" は以下の用に書けるようになりました。

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])
}

おわりに

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

1
0
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
1
0