Template Literal Types in TypeScript

August 17, 20253 min read
Requirements:
Generics| UnionsMapped Types

Building strings the type safe way

In JavaScript you have probably used template literals with backticks to build strings at runtime. Template literal types in TypeScript bring that same idea to the type system. Instead of building values they build string types by combining and transforming other types. Think of them as Lego bricks for type level string construction.

Why this matters

String values often follow predictable patterns. For example:

Without template literal types you rely on loose string types or manual unions to represent them. That means typos slip through. With template literal types you can express the exact allowed strings and get compiler errors if you stray.

The core idea

A template literal type uses backtick syntax with ${} placeholders but inside a type declaration. You can insert unions or other literal types inside the placeholders to generate combinations.

type Greeting = `Hello ${'World' | 'TypeScript'}`
// "Hello World" | "Hello TypeScript"

The compiler expands this into a union of all possible combinations.

Three practical moves

  1. Prefixing and suffixing Use them to generate key names with consistent patterns.

    type EventName = `on${Capitalize<'click' | 'hover'>}`
    // "onClick" | "onHover"

    Perfect for event handler props in React or custom APIs.

  2. Combining with unions Mix and match multiple unions to produce cross product strings.

    type Size = 'small' | 'medium' | 'large'
    type Variant = 'primary' | 'secondary'
    type ClassName = `${Size}-${Variant}`
    // "small-primary" | "small-secondary" | "medium-primary" ...

    This ensures only valid combinations are allowed.

  3. Integrating with mapped types You can rename keys or build derived object types.

    type WithApiPaths<T extends string> = {
      [K in T as `/api/${K}`]: string
    }
    type Endpoints = WithApiPaths<'users' | 'posts'>
    // { "/api/users": string; "/api/posts": string }

    This keeps route strings consistent across the codebase.

Contrast with plain string

A plain string type says “anything goes.” A manual union can be acucrate but is tedious to update when the base options change. Template literal types generate the exact set of allowed strings automatically from smaller pieces. It is the difference between writing out every option by hand versus letting the type system do the heavy lifting.

A micro utility you can reuse

type DashedKeys<T extends string> = `${Lowercase<T>}-${Lowercase<T>}`

Takes a string literal type and produces a dashed pair version like "name-name". You can tweak it to join different values or apply Uppercase and Capitalize built in helpers.

Wrap up

Template literal types let you define string patterns once and trust TypeScript to enforce them everywhere. They shine when you have structured naming schemes or string formats that repeat across your app. Start small by prefixing or suffixing string unions then explore combinations with mapped types for more power. If you have a neat use case for these I would love to compare notes.

Share this article

Stay Updated

Get the latest TypeScript tips, tutorials, and course updates delivered straight to your inbox.

No spam, unsubscribe at any time.