TypeScript's template literal types allows us to do define a template about how our strings should look like:
type EmailNameType = 'welcome' | 'subscribe' | 'marketing'
// 'welcome_email' | 'subscribe_email' | 'marketing_email'
type EmailTypes = `${EmailNameType}_email`
You can even include union types inside your template literals:
type EmailNameType = 'welcome' | 'subscribe' | 'marketing'
type AdminEmailNameType = 'new_account' | 'ticket_created'
// 'welcome_email' | 'subscribe_email' | 'marketing_email' | 'new_account_email' | 'ticket_created_email'
type EmailTypes = `${EmailNameType | AdminEmailNameType}_email`
TypeScript includes 4 helpers to manipulate string types:
Below you can see an example of each one of them:
Uppercase<'giovanni benussi'> // 'GIOVANNI BENUSSI'
Lowercase<'GIOVANNI Benussi'> // 'giovanni benussi'
Capitalize<'giovanni benussi'> // 'Giovanni benussi'
Uncapitalize<'Giovanni Benussi'> // 'giovanni Benussi'
You can combine the concepts that we just learn to build flexible types. For example, we can define event handlers in a convenient way:
type EventName = 'change' | 'click'
type EventType = `on${Capitalize(EventName)}`
function listen(event: EventType) {
// return ...
}
listen('onChange') // ✅
listen('onClick') // ✅
listen('onAnotherEvent') // ❌
As we just learned, TypeScript provides some handy ways to manipulate strings to make our code flexible. You could always write everything by hand, but having some automation may help you keep your code DRY.
If you want to learn more about template literal types, I highly suggest you to take a look at TypeScript's official docs on the subject.
As always, feel free to reach me out on Twitter if you have any question!