Los template literals, introducidos en ES6, resuelven los problemas históricos de JavaScript con strings multilínea e interpolación. Los tagged templates llevan esto mucho más lejos: permiten preprocesar el template con una función, lo que abre la puerta a SQL parametrizado, HTML seguro automático, sistemas de internacionalización y librerías como styled-components.
Template literals: interpolación y multilínea
Los template literals usan backticks (`) en lugar de comillas. Permiten incrustar expresiones con ${} y escribir strings que se extienden en varias líneas manteniendo los saltos:
const nombre = 'Ana';
const edad = 30;
// Interpolación (cualquier expresión válida dentro de ${})
console.log(`Hola, ${nombre}. Tienes ${edad} años.`);
console.log(`El doble de tu edad es ${edad * 2}`);
console.log(`Mayúsculas: ${nombre.toUpperCase()}`);
// Multilínea (los saltos de línea se preservan)
const html = `
${nombre}
Edad: ${edad}
`;
// Expresiones complejas: ternario, métodos, funciones
const precio = 29.99;
const iva = 0.21;
const mensaje = `Total: ${(precio * (1 + iva)).toFixed(2)} `;
console.log(mensaje); // "Total: 36.29 "
// Comparación con el operador +:
const viejo = 'Hola, ' + nombre + '. Tienes ' + edad + ' años.';
const nuevo = `Hola, ${nombre}. Tienes ${edad} años.`;
Tagged templates: función que procesa el template
Un tagged template es una función que recibe el template dividido en sus partes. La función recibe: un array de las partes literales del string, y los valores interpolados como argumentos adicionales:
function etiqueta(partes, ...valores) {
console.log(partes); // Array de strings literales
console.log(valores); // Array de valores interpolados
// Reconstruir el string (comportamiento por defecto)
return partes.reduce((acc, parte, i) => {
return acc + (valores[i - 1] ?? '') + parte;
});
}
const x = 5, y = 10;
etiqueta`El resultado de ${x} más ${y} es ${x + y}`;
// partes: ['El resultado de ', ' más ', ' es ', '']
// valores: [5, 10, 15]
HTML seguro: escapado automático con tagged templates
Uno de los casos de uso más importantes: escapar automáticamente todos los valores interpolados para prevenir XSS:
function html(partes, ...valores) {
const escapar = (str) =>
String(str)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
return partes.reduce((acc, parte, i) => {
return acc + (i > 0 ? escapar(valores[i - 1]) : '') + parte;
});
}
const nombreUsuario = '';
const saludo = html`Hola, ${nombreUsuario}!`;
console.log(saludo);
//
Hola, <script>alert("XSS")</script>!
SQL parametrizado con tagged templates
function sql(partes, ...valores) {
const query = partes.reduce((acc, parte, i) => {
return acc + (i > 0 ? `$${i}` : '') + parte;
});
return { query, params: valores };
}
const id = 42;
const activo = true;
const { query, params } = sql`
SELECT nombre, email
FROM usuarios
WHERE id = ${id} AND activo = ${activo}
`;
console.log(query);
// SELECT nombre, email FROM usuarios WHERE id = $1 AND activo = $2
console.log(params); // [42, true]
// Uso con pg (PostgreSQL para Node.js):
// const resultado = await client.query(query, params);
styled-components y sistemas de i18n
Los tagged templates son la base de styled-components en React y de muchos sistemas de internacionalización:
// styled-components (simplificado):
function css(partes, ...valores) {
return partes.reduce((acc, parte, i) => {
const valor = i < valores.length
? (typeof valores[i] === 'function' ? valores[i]() : valores[i])
: '';
return acc + valor + parte;
});
}
// En styled-components real:
// const Boton = styled.button`
// background: ${props => props.primary ? 'blue' : 'white'};
// color: ${props => props.primary ? 'white' : 'blue'};
// `;
// Sistema de i18n:
const traducciones = { 'Hola, {0}!': 'Hello, {0}!' };
function i18n(partes, ...valores) {
const clave = partes.join('{0}');
const traducida = traducciones[clave] || clave;
return traducida.replace(/{(d+)}/g, (_, i) => valores[parseInt(i)]);
}
const usuario = 'Ana';
console.log(i18n`Hola, ${usuario}!`); // 'Hello, Ana!'
La propiedad .raw del primer argumento contiene las partes del template sin procesar las secuencias de escape, útil para escribir etiquetas que trabajan con regex o rutas de archivo donde los backslashes no deben interpretarse.
