Table of Contents
- Introduction
- What Are Template Literal Types?
- Basic Syntax of Template Literal Types
- Combining Static and Dynamic Parts in Template Literals
- Advanced Usage of Template Literals
- Creating String Patterns
- Conditional Types with Template Literals
- Working with String Literals and Unions
- Using Template Literal Types with Enums and Other Types
- Practical Examples
- Example 1: Enforcing String Patterns
- Example 2: Combining Union Types and Template Literals
- Best Practices for Using Template Literal Types
- Conclusion
Introduction
Template literal types in TypeScript are a powerful feature that allows you to construct string types dynamically by combining literal strings with expressions. They offer the flexibility to define types that match specific patterns of strings, similar to how template literals work in JavaScript, but with the added power of type safety.
This feature can be used to enforce rules on strings, generate types based on patterns, and even combine multiple types into one. By the end of this article, you’ll understand how to leverage template literal types to create more precise and dynamic string typings in TypeScript.
What Are Template Literal Types?
Template literal types enable the creation of string types based on the structure of string literals. They allow you to express types that resemble template literals in JavaScript, but with the added ability to include type expressions.
A template literal type is defined using the `backtick (“) syntax and can combine literal types with placeholders for type expressions, like so:
type Greeting = `Hello, ${string}!`;
In this case, Greeting
is a type that can represent any string starting with “Hello, ” and followed by any string, finishing with an exclamation mark.
Basic Syntax of Template Literal Types
The basic syntax for a template literal type is as follows:
type SomeType = `prefix-${string}-suffix`;
Here, SomeType
can represent any string that starts with “prefix-“, followed by any string (string
), and ends with “-suffix”.
In more detail:
${}
allows you to embed expressions or types inside the string template.string
is a type that represents any string, but you can use more specific types as needed.
Example:
type FileName = `file-${number}.txt`;
Here, FileName
represents any string that matches the pattern of "file-"
, followed by a number
, and ending with ".txt"
.
Combining Static and Dynamic Parts in Template Literals
Template literal types can combine static string parts (literal text) with dynamic parts (type placeholders). This allows you to enforce specific patterns while still allowing flexibility in the type.
Example:
type DateString = `2022-${'01' | '02' | '03'}-${string}`;
In this example, DateString
is a type that expects a string beginning with 2022-
, followed by one of the strings “01”, “02”, or “03”, and then a hyphen and any other string. This is useful when you want to represent dates with specific months but without rigidly defining the full date structure.
Advanced Usage of Template Literals
Template literal types can be combined with other TypeScript features, such as conditional types and union types, to create more complex and flexible string patterns.
Creating String Patterns
One common use case of template literal types is to enforce a specific pattern in strings. You can combine literal types, unions, and other types to create a string pattern that matches certain conditions.
type UserRole = `admin-${string}` | `user-${string}`;
In this example, UserRole
can be any string that starts with admin-
or user-
and is followed by any string. This is useful for user roles that require a dynamic identifier but follow a predefined structure.
Conditional Types with Template Literals
Template literal types can also be used with conditional types to create complex patterns based on conditions.
type Status = 'active' | 'inactive';
type StatusMessage = Status extends 'active' ? `User is ${Status}` : `User is ${Status}`;
Here, the StatusMessage
type will either be User is active
or User is inactive
, depending on the value of the Status
.
Working with String Literals and Unions
You can combine template literal types with union types to generate even more flexible string patterns. A union of template literal types allows you to define a range of valid string patterns.
Example:
type ButtonType = `primary-${'submit' | 'reset'}` | `secondary-${'submit' | 'reset'}`;
In this case:
ButtonType
will allow any string that starts withprimary-
orsecondary-
, followed by eithersubmit
orreset
.- This can represent different types of buttons in a UI, ensuring that the string matches the pattern for button types.
Using Template Literal Types with Enums and Other Types
Template literal types can also be combined with other types like enums and type aliases to create more complex and reusable patterns.
Example: Template Literals with Enums
enum FileType {
JPEG = 'jpeg',
PNG = 'png',
GIF = 'gif'
}
type FileName = `image-${FileType}.${string}`;
Here:
FileName
is a type that expects strings starting withimage-
, followed by one of the values from theFileType
enum, and ending with any string (e.g.,"image-jpeg.abc"
).
By combining enums with template literals, you can create dynamic, but well-defined string patterns that represent structured data.
Practical Examples
Example 1: Enforcing String Patterns
type EmailPattern = `${string}@${string}.${'com' | 'org' | 'net'}`;
In this example, EmailPattern
enforces a pattern where the string must contain an “@” symbol, followed by a domain name, and then ending with a top-level domain of either “com”, “org”, or “net”.
Example 2: Combining Union Types and Template Literals
type UserId = `${'user' | 'admin'}-${number}`;
This type represents a string that starts with either “user-” or “admin-” and is followed by a number. It’s a common pattern for user identification, ensuring both flexibility and consistency in the string format.
Example 3: Combining Multiple Patterns
type PhoneNumber = `+${number}-${number}-${number}`;
This template literal type enforces a phone number format like +123-456-7890
, where each part is a number, but the exact digits are flexible.
Best Practices for Using Template Literal Types
- Use Template Literals for Enforcing Patterns: Template literals are most useful when you need to enforce a specific string pattern, such as file names, user IDs, or status codes.
- Combine with Union Types for Flexibility: Combine template literals with union types to create more complex patterns and increase the flexibility of your types.
- Use with Enums for Strong Typing: When working with predefined sets of values, combining template literal types with enums can provide a type-safe way to generate structured strings.
- Be Aware of Performance: While template literals provide great flexibility, using them excessively in very large codebases might impact performance, so use them judiciously in scenarios that require dynamic string typing.
Conclusion
Template literal types are a powerful feature in TypeScript that allow you to define string types dynamically based on patterns. They give you the ability to enforce specific string formats, combine multiple types, and even use conditional logic to determine string content. By combining template literals with union types, enums, and other TypeScript features, you can create flexible and type-safe patterns for a variety of scenarios.
With the knowledge from this article, you can start applying template literal types to your own projects, ensuring better type safety and consistency in your code. Whether you’re enforcing URL formats, file names, or dynamic identifiers, template literal types offer a clean and efficient solution.