FrontendPrep
typescriptMedium

TypeScript Template Literal Types

Learn how Template Literal Types work in TypeScript and how to use them to create flexible and strongly-typed string formats.

TypeScript Template Literal Types

In JavaScript, we use template literals (e.g., `hello ${name}`) to build dynamic string values. In TypeScript, Template Literal Types bring this exact same concept to the type system, allowing you to create types based on string combinations.


1. What are Template Literal Types?

Template literal types use the backtick syntax (`) to define string type shapes. When you interpolate union types inside them, TypeScript automatically generates all possible combinations of those unions.

type Alignment = "left" | "center" | "right";
type VerticalAlignment = "top" | "middle" | "bottom";
 
// Generates all 9 combinations automatically
type Position = `${VerticalAlignment}-${Alignment}`;
/*
Result:
"top-left" | "top-center" | "top-right" | 
"middle-left" | "middle-center" | "middle-right" | 
"bottom-left" | "bottom-center" | "bottom-right"
*/

2. String Manipulation Utilities

TypeScript provides four built-in utility types to assist with template literal types:

  • Uppercase<S>: Converts a string type to UPPERCASE.
  • Lowercase<S>: Converts a string type to lowercase.
  • Capitalize<S>: Capitalizes the first letter of a string type.
  • Uncapitalize<S>: Converts the first letter of a string type to lowercase.
type Status = "pending" | "success";
 
type Action = `set${Capitalize<Status>}`;
// Result: "setPending" | "setSuccess"

3. Real-World Use Cases

Case A: Strongly-Typed Event Listeners

Suppose you are writing a custom event dispatcher. You want to enforce that all event callback helper names follow the pattern onEventName.

type Events = "click" | "hover" | "submit";
 
// Build listener names: "onClick" | "onHover" | "onSubmit"
type ListenerName<T extends string> = `on${Capitalize<T>}`;
 
interface EventDispatcher {
  // Enforces parameters like: dispatch("onClick", callback)
  dispatch<E extends Events>(name: ListenerName<E>, callback: () => void): void;
}
 
const dispatcher: EventDispatcher = {
  dispatch(name, callback) {
    console.log(`Registered ${name}`);
  }
};
 
// Valid calls
dispatcher.dispatch("onClick", () => {});
dispatcher.dispatch("onSubmit", () => {});
 
// Compile Error: Argument of type '"click"' is not assignable
// dispatcher.dispatch("click", () => {}); 

Case B: Typing CSS Property Spacings

If you are designing a UI component library, you can restrict prop values to valid tailwind-like padding combinations.

type Axis = "x" | "y";
type PaddingValue = 1 | 2 | 3 | 4;
 
// Generates: "px-1" | "px-2" | "py-1" | "py-4" etc.
type PaddingClassName = `p${Axis}-${PaddingValue}`;
 
interface ButtonProps {
  padding: PaddingClassName;
}
 
const btn: ButtonProps = {
  padding: "px-4" // Valid!
};
 
// Compile Error: Type '"px-5"' is not assignable to type PaddingClassName
// const invalidBtn: ButtonProps = { padding: "px-5" }; 

Key Takeaways

  • Template Literal Types construct string types by interpolating smaller string literals or unions.
  • Interpolating union types expands into every possible combination of strings.
  • Built-in helpers like Capitalize, Unuppercase, Lowercase, and Uppercase help conform to camelCase or UPPERCASE conventions.
  • They are highly useful for typing configuration keys, css class names, action creators, and event dispatchers.

Share this Resource

Help other developers level up by sharing this study guide.

More Technical Questions

Expand your mastery. Deep dive into other frontend interview challenges in this category.