How to use tagged templates - kdaisho/Blog GitHub Wiki
You might have seen something like this;
const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'gray'};
color: #fff;
padding: 1rem;
`;
That can be done with tagged templates syntax. It's similar to a function declaration, but it comes without parenthesis.
function format(strings, ...values) {
let output = "";
for (let i = 0; i < strings.length; i++) {
output += strings[i]
if (i < values.length) {
if (typeof values[i] === 'number') {
output += `<i>${values[i]}</i>`
} else {
output += `<b>${values[i]}</b>`
}
}
}
return output
}
let name = 'Maria'
let age = 30
format`Data: ${name} has been working since she was ${age}!`
// 'Data: <b>Maria</b> has been working since she was <i>30</i>!'
If you want to escape apostrophe '
by doubling it ''
;
function sql(strings, ...values) {
return strings.reduce((result, string, i) => {
let value = values[i - 1];
if (typeof value === 'string') {
value = `'${value.replace(/'/g, "''")}'`; // Escape single quotes
}
return result + value + string;
});
}
const table = 'users';
const column = 'name';
const value = "O'Reilly";
sql`SELECT * FROM ${table} WHERE ${column} = ${value}`;
// SELECT * FROM 'users' WHERE 'name' = 'O''Reilly'
type User = {
name?: string
email: string
phone?: string
}
const user1: User = {
email: '[email protected]',
}
const user2: User = {
name: 'Daisho Komiyama',
email: '[email protected]',
}
const user3: User = {
name: 'Jason Smith',
email: '[email protected]',
phone: '123-456-7890',
}
const user4: User = {
email: '[email protected]',
phone: '911',
}
function label(strings: TemplateStringsArray, ...values: User[]) {
let output = ''
for (let i = 0; i < strings.length; i++) {
output += strings[i]
if (values[i]) {
if (values[i].name) {
output += values[i].name
} else {
output += values[i].email
}
if (values[i].phone) {
output += `, phone: ${values[i].phone}`
}
}
}
return output
}
console.log(label`Our primary customer is ${user1} and all good.`)
// Our primary customer is someone@gmail.com and all good.
console.log(label`Our primary customer is ${user2} and all good.`)
// Our primary customer is Daisho Komiyama and all good.
console.log(label`Our primary customer is ${user3} and all good.`)
// Our primary customer is Jason Smith, phone: 123-456-7890 and all good.
console.log(label`Our primary customer is ${user4} and all good.`)
// Our primary customer is spvm@gmail.com, phone: 911 and all good.
If you want to do above without tagged template, you would end up with a lot of ?.
and ??
operators just like below examples.
console.log(`Our primary customer is ${user1?.name ?? user1.email}${user1?.phone ? `, phone: ${user1?.phone}` : ''} and all good.`)
console.log(`Our primary customer is ${user2?.name ?? user2.email}${user2?.phone ? `, phone: ${user2?.phone}` : ''} and all good.`)
console.log(`Our primary customer is ${user3?.name ?? user3.email}${user3?.phone ? `, phone: ${user3?.phone}` : ''} and all good.`)
console.log(`Our primary customer is ${user4?.name ?? user4.email}${user4?.phone ? `, phone: ${user4?.phone}` : ''} and all good.`)
It's not only hard to read but also error-prone. Tagged template is a great way to handle this kind of situation.
function incidentTag(strings: TemplateStringsArray, ...values: Date[] | string[]) {
const outputArray = values.map(
(value, i) =>
`${strings[i]}${
value instanceof Date
? value.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})
: value
}`
)
return outputArray.join('') + strings[strings.length - 1]
}
const date1 = new Date('Aug 14, 2002')
const date2 = 'Dec 25, 1977'
console.log(incidentTag`The incident occurred on ${date1}.`)
// The incident occurred on Aug 14, 2002.
console.log(incidentTag`The incident occurred on ${date2}.`)
// The incident occurred on Dec 25, 1977.
enum UserStatus = {
INACTIVE = 0,
ACTIVE = 1,
ARCHIVED = 2,
}
export function tag(
strings: TemplateStringsArray,
...placeholders: (Record<string, string | UserStatus> | null)[]
): string {
return strings.reduce((acc, str, i) => {
acc += str;
if (typeof placeholders[i] === 'object') { // null is also 'object'
acc += placeholders[i]?.name ?? placeholders[i]?.email ?? 'an user';
}
return acc;
}, '');
}