Poznajemy tagged template literals w JavaScript – mało znaną, ale potężną i niedocenianą właściwość tego języka. Do dzieła.

Ok, mały przykład jak to działa:

function tagged1(strings){
    console.log(strings);
}

tagged1`Hello World 123 !`;
//[ "Hello World 123 !" ]
tagged1`Hello World ${2+2} Hi yolo`;
//[ "Hello World ", " Hi yolo" ]

Po pierwsze – tak, to działa. Po drugie, w strings lądują elementy tekstowe niebędące wyrażeniem. Jeżeli nie ma wyrażeń, to cały string jako tablica jednoelementowa tam jest.

Ok, a co z wyrażeniami? Możemy je zbierać pojedynczo, ale ja wolę też je do tablicy zapakować:

function tagged2(strings, ...expr){
    console.log(strings);
    console.log(expr);
}

tagged2`Hello World ${2+2} Hi yolo`;
//[ "Hello World ", " Hi yolo" ]
//Array [ 4 ]

Teraz do czego nam to. W internecie znalazłem taką funkcję:

function highlight(strings, ...values) {
  let str = '';
  strings.forEach((string, i) => {
    str += string + values[i];
  });
  return str;
}

Niby fajnie, tylko tu robimy dwa założenia:

  • Wyrażenie nigdy nie będzie przed napisem
  • Ilość wyrażeń i napisów będzie równa

Ok, sprawdźmy to pierwsze, czyli co się dzieje gdy dodamy wyrażenie przed napisem:

function tagged2(strings, ...expr){
    console.log(strings);
    console.log(expr);
}

tagged2`${2+2}Hello World ${2+2} Hi yolo`;
//[ "", "Hello World ", " Hi yolo" ]
//Array [ 4, 4 ]

No to akurat nie problem jak widać – strings po prostu zaczną się od pustego napisu. Jest to o tyle dobre, że kolejność dodawania mamy taką, że najpierw string, potem expr i nie chcemy, by cokolwiek ją popsuło.

Ale w przykładzie poprzednim widzieliśmy, że napisów może być więcej niż wyrażeń. Tutaj też widzimy, mamy pusty „” i dwa napisy, razem trzy, zaś wyrażenia tylko 2.

Tu już jest lepszy przykład (od tego samego autora):

function highlight(strings, ...values) {
  let str = '';
  strings.forEach((string, i) => {
    str += `${string} <span class='hl'>${values[i] || ''}</span>`;
  });
  return str;
}

Natomiast ten kod będzie puste spany produkował. Inny przykład od tego autora:

function highlight(strings, ...values) {
  let str = '';
  strings.forEach((string, i) => {
    str += string + (values[i] || '');
  });
  return str;
}

To już jest dobry trop. Cóż, po coś my te replacementy zbieramy i pakujemy do jednej tablicy. Możemy na niej użyć np. map:

function highlight(strings, ...expr){

    let str = "";
    let highlightedExpr = expr.map((el) => `<strong>${el}</strong>`);
     
    strings.forEach((string, i) => {
        str += string + (highlightedExpr[i] || '');
      });

      return str;
}

let highlighted = highlight`Hello World ${2+2} Hi yolo`;

console.log(highlighted);
//Hello World <strong>4</strong> Hi yolo

I tu mamy takie coś, co niezależnie od tego czy liczba tekstów pokrojonych wyrażeniami i replacementów się zgadza czy nie poskleja wszystko do kupy, zaś replacementy będą w tagu strong.

Przeanalizujmy to sobie na spokojnie, bo nie jest to łatwe.