Tagged template literals
LidoNesta aula, você aprenderá sobre tagged template literals em JavaScript, uma funcionalidade poderosa que permite processar e transformar strings de forma personalizada. Exploraremos como funcionam, casos de uso práticos como sanitização e internacionalização, e exemplos detalhados para consolidar o conhecimento.
As tagged template literals (ou literais de modelo com etiqueta) são uma funcionalidade avançada do JavaScript que permite que você processe template literals de uma maneira personalizada, através de uma função (a "tag"). Em vez de simplesmente interpolar valores em uma string, você pode passar tanto as partes literais da string quanto os valores interpolados para uma função, que pode transformá-los, validá-los ou até mesmo retornar algo completamente diferente. Isso abre um leque de possibilidades para criar DSLs (linguagens específicas de domínio), sanitizar entradas de usuário, implementar sistemas de internacionalização e muito mais.
Antes de mergulharmos nos detalhes, é importante ter uma boa compreensão de template literals básicos, que são strings delimitadas por crases (``) e que permitem interpolação de expressões usando ${expressao}. As tagged template literals estendem esse conceito, adicionando uma função antes da string. Por exemplo: tag`Olá, ${nome}!`. A função tag recebe os pedaços da string e os valores interpolados, podendo processá-los de qualquer forma.
Como funcionam
Quando você usa uma tagged template literal, a função é chamada com dois tipos de argumentos: o primeiro é um array de strings (as partes literais da string, separadas pelos pontos de interpolação) e os argumentos seguintes são os valores interpolados (as expressões dentro de ${...}). A ordem é sempre: primeiro o array de strings, depois cada valor na ordem em que aparece.
Vamos a um exemplo simples:
function minhaTag(strings, ...valores) {
console.log(strings); // Array de strings
console.log(valores); // Array de valores
return "Resultado processado";
}
const nome = "Maria";
const idade = 30;
const resultado = minhaTag`Nome: ${nome}, Idade: ${idade}`;
console.log(resultado); // "Resultado processado"
No exemplo acima, strings será ['Nome: ', ', Idade: ', ''] e valores será ['Maria', 30]. Note que o array de strings sempre tem um elemento a mais que o número de valores, porque as strings são separadas pelos valores. Isso é fundamental para reconstruir a string original, se necessário.
Outra característica importante é que as strings têm uma propriedade raw no array de strings, que contém as versões "cruas" das strings, sem processamento de escapes (por exemplo, \n permanece como dois caracteres, não como quebra de linha). Isso é útil para processar código ou texto onde os escapes devem ser preservados.
function tag(strings, ...valores) {
console.log(strings.raw[0]); // Exibe a string crua
return strings.raw[0] + valores.join(" ");
}
const texto = tag`Linha 1\nLinha 2`;
console.log(texto); // Linha 1\nLinha 2 (com \n literal)
Essa capacidade de acessar as strings cruas é essencial para ferramentas que precisam interpretar o conteúdo literal, como compiladores ou processadores de linguagem.
Casos de uso (sanitização, i18n)
As tagged template literals são extremamente versáteis. Dois casos de uso muito comuns são a sanitização de strings (para evitar injeção de código ou HTML) e a internacionalização (i18n). Vamos explorar cada um.
Sanitização
Quando você insere valores de usuário em uma string que será usada em HTML, SQL ou outros contextos, é crucial escapar caracteres perigosos para evitar ataques como XSS (Cross-Site Scripting) ou injeção de SQL. Com tagged templates, você pode criar uma função que automaticamente escapa os valores interpolados, garantindo que sejam seguros.
function escaparHTML(strings, ...valores) {
const escapar = (valor) => {
return String(valor)
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};
return strings.reduce((resultado, string, i) => {
return resultado + string + (i < valores.length ? escapar(valores[i]) : "");
}, "");
}
const usuario = "";
const saudacao = escaparHTML`Olá, ${usuario}!`;
console.log(saudacao); // Olá, <script>alert('XSS')</script>!
Nesse exemplo, a função escaparHTML substitui caracteres especiais por entidades HTML, neutralizando qualquer código malicioso. Isso é muito mais limpo e seguro do que tentar lembrar de escapar cada interpolação manualmente.
Internacionalização (i18n)
Em aplicações multilíngues, você pode usar tagged templates para escolher a tradução correta com base em um idioma, além de formatar números e datas de acordo com o local. A tag pode receber um objeto de traduções ou o idioma atual e retornar a string traduzida.
const traducoes = {
pt: {
saudacao: (nome) => `Olá, ${nome}!`,
despedida: (nome) => `Até logo, ${nome}!`,
},
en: {
saudacao: (nome) => `Hello, ${nome}!`,
despedida: (nome) => `Goodbye, ${nome}!`,
},
};
function i18n(strings, ...valores) {
const idioma = i18n.idiomaAtual || "pt";
const chave = strings.join("").trim(); // Ex.: "saudacao"
const funcao = traducoes[idioma][chave];
if (typeof funcao === "function") {
return funcao(...valores);
}
return strings.join(" "); // fallback
}
i18n.idiomaAtual = "pt";
console.log(i18n`saudacao ${ "João" }`); // Olá, João!
i18n.idiomaAtual = "en";
console.log(i18n`despedida ${ "John" }`); // Goodbye, John!
Nesse exemplo, a tag i18n usa o primeiro segmento da string como chave de tradução e os valores como argumentos para a função de tradução. Isso permite escrever templates que são automaticamente traduzidos, mantendo o código limpo e legível.
Exemplos
Vamos ver mais alguns exemplos práticos de tagged template literals, incluindo formatação de moeda, criação de HTML dinâmico e construção de consultas SQL seguras.
Formatação de moeda
Você pode criar uma tag que formata números como moeda usando o objeto Intl.NumberFormat.
function moeda(strings, ...valores) {
const formatador = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
});
let resultado = "";
strings.forEach((string, i) => {
resultado += string;
if (i < valores.length) {
const valor = Number(valores[i]);
resultado += formatador.format(valor);
}
});
return resultado;
}
const preco = 1234.5;
console.log(moeda`O preço é ${preco}`); // O preço é R$ 1.234,50
Criação de HTML dinâmico
Você pode construir templates HTML com segurança, escapando valores e criando elementos dinamicamente.
function html(strings, ...valores) {
const escapar = (valor) => {
return String(valor)
.replace(/&/g, "&")
.replace(//g, ">");
};
let resultado = "";
strings.forEach((string, i) => {
resultado += string;
if (i < valores.length) {
resultado += escapar(valores[i]);
}
});
return resultado;
}
const usuario = { nome: "Ana", email: "ana@example.com" };
const card = html`
${usuario.nome}
${usuario.email}
`;
console.log(card);
Consultas SQL seguras
Em Node.js, você pode usar tagged templates para criar consultas SQL parametrizadas, evitando injeção de SQL.
const sql = (strings, ...valores) => {
// Simulação: retorna a consulta com placeholders
return strings.reduce((acc, string, i) => {
return acc + string + (i < valores.length ? "?" : "");
}, "");
};
const id = 1;
const nome = "João";
const query = sql`SELECT * FROM usuarios WHERE id = ${id} AND nome = ${nome}`;
console.log(query); // SELECT * FROM usuarios WHERE id = ? AND nome = ?
Nesse exemplo, a tag sql substitui os valores por placeholders ?, que depois podem ser passados a um driver de banco de dados que os preenche de forma segura.
Boas práticas e observações finais
Ao usar tagged template literals, é importante considerar alguns pontos:
- Nomes descritivos: dê nomes claros às suas tags para indicar o que elas fazem (ex.:
html,sql,i18n). - Performance: em loops ou chamadas frequentes, evite criar funções desnecessárias dentro da tag, pois isso pode impactar o desempenho. Prefira funções definidas fora.
- Teste: como as tags podem transformar strings de formas inesperadas, escreva testes para garantir que o comportamento está correto, especialmente quando usadas em segurança.
- Compatibilidade: tagged template literals são suportados em todos os ambientes modernos (Node.js 4+, navegadores atuais). Para ambientes mais antigos, use transpiladores como Babel.
Dominar tagged template literals é um passo importante para escrever JavaScript mais expressivo e robusto. Eles permitem criar APIs elegantes e seguras, reduzindo a probabilidade de erros e ataques. Experimente usá-los em seus projetos e veja como podem simplificar tarefas complexas.
Referências
- MDN: Template literals
- MDN: Template literals com etiquetas
- MDN: String.raw
- MDN: Intl (Internationalization API)
- MDN: Intl.NumberFormat
- Node.js: SQLite (para exemplo de SQL)
Exercícios
- Exercício 1: Escreva uma tagged template literal chamada
maiusculaque transforme todos os valores interpolados em letras maiúsculas, mas mantenha as strings literais inalteradas. Teste commaiuscula`Olá, ${nome}!`ondenome = "mundo". - Exercício 2: Crie uma tag
destacarque envolva cada valor interpolado em<strong>e</strong>para uso em HTML, mas escape o valor para evitar XSS. Utilize a função de escape do exemplo de sanitização.
function maiuscula(strings, ...valores) {
let resultado = "";
strings.forEach((string, i) => {
resultado += string;
if (i < valores.length) {
resultado += String(valores[i]).toUpperCase();
}
});
return resultado;
}
const nome = "mundo";
console.log(maiuscula`Olá, ${nome}!`); // Olá, MUNDO!
function destacar(strings, ...valores) {
const escapar = (valor) => {
return String(valor)
.replace(/&/g, "&")
.replace(//g, ">");
};
let resultado = "";
strings.forEach((string, i) => {
resultado += string;
if (i < valores.length) {
resultado += `${escapar(valores[i])}`;
}
});
return resultado;
}
const texto = "