Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Cheat Sheets
  3. TypeScript
  4. TypeScript Template Literal Types Cheat Sheet
TypeScriptCheat Sheet

TypeScript Template Literal Types Cheat Sheet

String manipulation at the type level -- template literal types, built-in string utilities, key remapping, and real-world patterns.

On this page
  1. 1Basic Template Literal Type
  2. 2String Union Combinations
  3. 3Built-in String Manipulation Types
  4. 4Key Remapping with Template Literals
  5. 5Pattern Matching with Template Literals
  6. 6Real-World Patterns
  7. 7Limits and Gotchas
Basic Template Literal TypeString Union CombinationsBuilt-in String Manipulation TypesKey Remapping with Template LiteralsPattern Matching with Template LiteralsReal-World PatternsLimits and Gotchas

Basic Template Literal Type

Template literal types use the same backtick syntax as JavaScript template literals, but at the type level.

String interpolation in types
type Greeting = `Hello, ${string}`;

const a: Greeting = 'Hello, Alice';   // OK
const b: Greeting = 'Hello, Bob';     // OK
// const c: Greeting = 'Hi, Alice';   // Error: doesn't match pattern
Combining literal types
type Vertical = 'top' | 'bottom';
type Horizontal = 'left' | 'right';

type Position = `${Vertical}-${Horizontal}`;
// => 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'

const pos: Position = 'top-left';     // OK
// const bad: Position = 'center';    // Error
With numeric literal types
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type HexPrefix = `0x${Digit}${Digit}`;
// => '0x00' | '0x01' | ... | '0x99'

const hex: HexPrefix = '0x1F';  // Error: not in the union
const ok: HexPrefix = '0x42';   // OK

Template literal types distribute over unions. Each member of each union slot combines with every member of other slots, creating a cartesian product.

String Union Combinations

Combine two or more string literal unions to generate every possible combination automatically.

CSS property generator
type Size = 'sm' | 'md' | 'lg';
type Side = 'top' | 'right' | 'bottom' | 'left';

type SpacingClass = `${Side}-${Size}`;
// => 'top-sm' | 'top-md' | 'top-lg' | 'right-sm' | ... (12 total)

const cls: SpacingClass = 'bottom-lg';  // OK
Event handler names
type DOMEvent = 'click' | 'focus' | 'blur' | 'change';

type Handler = `on${Capitalize<DOMEvent>}`;
// => 'onClick' | 'onFocus' | 'onBlur' | 'onChange'

const handler: Handler = 'onClick';  // OK
Prefixed/suffixed variations
type Entity = 'user' | 'post' | 'comment';

type Getter = `get${Capitalize<Entity>}`;     // 'getUser' | 'getPost' | 'getComment'
type CreatedEvent = `${Entity}Created`;        // 'userCreated' | 'postCreated' | 'commentCreated'
type TableName = `${Entity}s`;                 // 'users' | 'posts' | 'comments'

Built-in String Manipulation Types

TypeScript provides four intrinsic types that transform string literal types. They work on individual literals and distribute over unions.

Uppercase and Lowercase
type Upper = Uppercase<'hello'>;      // => 'HELLO'
type Lower = Lowercase<'HELLO'>;      // => 'hello'

type Shout = Uppercase<'yes' | 'no'>; // => 'YES' | 'NO'

// Practical: environment variable names
type EnvKey = 'apiUrl' | 'dbHost';
type EnvVar = `NEXT_PUBLIC_${Uppercase<EnvKey>}`;
// => 'NEXT_PUBLIC_APIURL' | 'NEXT_PUBLIC_DBHOST'
Capitalize and Uncapitalize
type Cap = Capitalize<'hello'>;          // => 'Hello'
type Uncap = Uncapitalize<'Hello'>;      // => 'hello'

// Practical: getter/setter names from field names
type Field = 'name' | 'email' | 'age';

type Getter = `get${Capitalize<Field>}`;
// => 'getName' | 'getEmail' | 'getAge'

type Setter = `set${Capitalize<Field>}`;
// => 'setName' | 'setEmail' | 'setAge'
Chaining string utilities
// Screaming snake case from camelCase words
type Key = 'userId' | 'userName';

type ScreamSnake = Uppercase<Key>;
// => 'USERID' | 'USERNAME'

// These operate on individual characters only --
// they don't add underscores or split words.
// For full case conversion, you need recursive conditional types.

These four types are compiler intrinsics -- they run at the type level during compilation. You can't define custom string manipulation types with the same performance.

Key Remapping with Template Literals

The `as` clause in mapped types lets you rename keys using template literal types. Added in TypeScript 4.1.

Prefix every key
type Prefixed<T> = {
  [K in keyof T as `data_${string & K}`]: T[K];
};

interface User {
  name: string;
  age: number;
}

type PrefixedUser = Prefixed<User>;
// => { data_name: string; data_age: number }
Generate getter methods
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface Config {
  host: string;
  port: number;
  debug: boolean;
}

type ConfigGetters = Getters<Config>;
// => {
//   getHost: () => string;
//   getPort: () => number;
//   getDebug: () => boolean;
// }
Filter keys by remapping to never
// Remove keys whose values are functions
type DataOnly<T> = {
  [K in keyof T as T[K] extends Function ? never : K]: T[K];
};

interface UserService {
  name: string;
  age: number;
  save(): Promise<void>;
  delete(): Promise<void>;
}

type UserData = DataOnly<UserService>;
// => { name: string; age: number }

Remapping a key to `never` removes it from the output type. This is how you filter properties in mapped types.

Pattern Matching with Template Literals

Use `infer` inside template literal types to extract parts of string types. Think of it as regex for the type system.

Extract parts of a string type
type ExtractId<T> = T extends `user_${infer Id}` ? Id : never;

type A = ExtractId<'user_123'>;    // => '123'
type B = ExtractId<'user_abc'>;    // => 'abc'
type C = ExtractId<'post_456'>;    // => never (no match)
Split on delimiter
type Split<S extends string, D extends string> =
  S extends `${infer Head}${D}${infer Tail}`
    ? [Head, ...Split<Tail, D>]
    : [S];

type Parts = Split<'a.b.c', '.'>;    // => ['a', 'b', 'c']
type Words = Split<'hello world', ' '>; // => ['hello', 'world']
Typed route parameters
type ExtractParams<T extends string> =
  T extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractParams<Rest>
    : T extends `${string}:${infer Param}`
      ? Param
      : never;

type Params = ExtractParams<'/users/:userId/posts/:postId'>;
// => 'userId' | 'postId'

// Use it to build a typed params object:
type RouteParams<T extends string> = {
  [K in ExtractParams<T>]: string;
};

type UserPostParams = RouteParams<'/users/:userId/posts/:postId'>;
// => { userId: string; postId: string }

Recursive template literal types are limited to about 1000 recursion levels. For deeply nested strings, TypeScript will bail out with a "type instantiation is excessively deep" error.

Real-World Patterns

Practical template literal type patterns from production TypeScript codebases.

CSS unit types
type CSSUnit = 'px' | 'rem' | 'em' | '%' | 'vh' | 'vw';
type CSSValue = `${number}${CSSUnit}` | '0' | 'auto';

function setWidth(value: CSSValue): void {
  // value is guaranteed to be a valid CSS length
}

setWidth('16px');     // OK
setWidth('1.5rem');   // OK
setWidth('100%');     // OK
setWidth('auto');     // OK
// setWidth('big');   // Error
Typed API routes
type APIBase = '/api/v1';
type Resource = 'users' | 'posts' | 'comments';
type APIRoute = `${APIBase}/${Resource}`;
// => '/api/v1/users' | '/api/v1/posts' | '/api/v1/comments'

type WithId = `${APIRoute}/${string}`;
// => '/api/v1/users/...' | '/api/v1/posts/...' | ...

async function fetchResource(route: APIRoute): Promise<unknown> {
  return fetch(route).then((r) => r.json());
}

fetchResource('/api/v1/users');   // OK
// fetchResource('/api/v2/users'); // Error
Typed event emitter
type EventConfig = {
  user: { id: string; name: string };
  post: { id: string; title: string };
};

type EventName = `${keyof EventConfig & string}:${'created' | 'updated' | 'deleted'}`;
// => 'user:created' | 'user:updated' | 'user:deleted'
//  | 'post:created' | 'post:updated' | 'post:deleted'

type ParseEvent<T extends string> =
  T extends `${infer Entity}:${infer Action}`
    ? { entity: Entity; action: Action }
    : never;

type Info = ParseEvent<'user:created'>;
// => { entity: 'user'; action: 'created' }

Limits and Gotchas

Template literal types are powerful but have sharp edges. Know these limits before going deep.

Combinatorial explosion
type Char = 'a' | 'b' | 'c' | 'd' | 'e';  // 5 members

type TwoChar = `${Char}${Char}`;    // 25 combinations -- fine
type ThreeChar = `${Char}${Char}${Char}`; // 125 combinations -- still OK

// type FourChar = `${Char}${Char}${Char}${Char}`; // 625 combinations
// This compiles but slows down the type checker noticeably.
// At 5+ slots with large unions, you'll hit the 100,000 member limit.

TypeScript caps union types at 100,000 members. Template literal types that generate unions beyond this limit produce an error: "Expression produces a union type that is too complex to represent."

Template literals only work with string/number/bigint/boolean
type Valid = `id_${number}`;     // OK
type AlsoValid = `is_${boolean}`; // OK: 'is_true' | 'is_false'

// type Invalid = `data_${object}`;  // Error
// type AlsoInvalid = `items_${string[]}`; // Error

// Only these types can appear in template literal slots:
// string, number, bigint, boolean, and literal subtypes thereof
Recursion depth limit
// Recursive template literal types have a depth limit (~1000)
type Repeat<S extends string, N extends number, Acc extends string = ''> =
  Acc extends { length: N }
    ? Acc
    : Repeat<S, N, `${Acc}${S}`>;

// This pattern works for small N but hits the recursion
// limit for large values. Design types to avoid deep recursion.
Learn TypeScript in Depth
Template Literal Types →
See Also
Utility Types →satisfies Operator →

Start Practicing TypeScript

Free daily exercises with spaced repetition. No credit card required.

← Back to TypeScript Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.