How to fix Cannot use expression in template literal type in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 8, 2025· 99 views

This TypeScript error occurs when you attempt to use an invalid expression or operation within a template literal type definition. Template literal types only allow specific type-level constructs like type parameters, unions, and intrinsic string manipulation types, not runtime expressions or complex type operations.

Quick fix

Fixes Cannot use expression in template literal type

// WRONG - conditional type directly in template
type Message<T> = \`Value is \${T extends string ? T : never}\`;
// Error: Cannot use expression in template literal type

// CORRECT - extract conditional to separate type
type ExtractString<T> = T extends string ? T : never;
type Message<T> = \`Value is \${ExtractString<T>}\`;

// Example usage:
type StringMsg = Message<"hello">;  // "Value is hello"
type NumberMsg = Message<42>;       // "Value is never"
  1. Extract complex type operations to separate type aliases
  2. Use only valid template literal type constructs
  3. Refactor property access to helper types
  4. Use mapped types outside template literals
typescript — error
// WRONG - conditional type directly in template
type Message<T> = \`Value is \${T extends string ? T : never}\`;
// Error: Cannot use expression in template literal type
// CORRECT - extract conditional to separate type
type ExtractString<T> = T extends string ? T : never;
type Message<T> = \`Value is \${ExtractString<T>}\`;
// Example usage:
type StringMsg = Message<"hello">; // "Value is hello"
type NumberMsg = Message<42>; // "Value is never"
Cannot use expression in template literal type

What this error means

The "Cannot use expression in template literal type" error appears when TypeScript encounters an expression that isn't valid within the template literal type syntax. Template literal types, introduced in TypeScript 4.1, allow you to create new string types by combining and transforming existing string literal types at the type level. However, template literal types have strict limitations on what can be placed inside the ${} interpolation syntax. You can only use: - Type parameters and generic type variables - Union types of string literals - Intrinsic string manipulation types (`Uppercase<T>`, `Lowercase<T>`, `Capitalize<T>`, `Uncapitalize<T>`) - Other template literal types - String literal types You cannot use: - Conditional types directly in the template - Array indexing or property access on types - Mapped types - Complex type operations - Runtime values or expressions This restriction exists because template literal types operate purely at the type level during compilation, and certain type operations cannot be safely or efficiently computed within the template literal syntax.

Typical symptoms

  • Compilation fails with 'Cannot use expression in template literal type' error
  • Red underlines appear in IDE on template literal type definitions
  • Error occurs within backtick-delimited type definitions using ${} syntax
  • Complex type operations inside template literal interpolation fail
  • Conditional types or mapped types used inside template literals are rejected

Common causes

  1. Using conditional types directly inside template literal type interpolation
  2. Attempting to access object properties or array indices within template literals
  3. Placing mapped types inside the ${} syntax of template literal types
  4. Using type operations that require evaluation in template literal context
  5. Trying to use typeof or other type queries inside template literal types
  6. Nesting complex type transformations that aren't supported in template context

How to fix "Cannot use expression in template literal type"

1Extract complex type operations to separate type aliases

Move conditional types and complex operations outside the template literal:

typescript
// WRONG - conditional type directly in template
type Message<T> = \`Value is \${T extends string ? T : never}\`;
// Error: Cannot use expression in template literal type

// CORRECT - extract conditional to separate type
type ExtractString<T> = T extends string ? T : never;
type Message<T> = \`Value is \${ExtractString<T>}\`;

// Example usage:
type StringMsg = Message<"hello">;  // "Value is hello"
type NumberMsg = Message<42>;       // "Value is never"

This approach separates the conditional logic from the template literal, which is a supported pattern.

2Use only valid template literal type constructs

Ensure you're only using supported expressions inside template literals:

typescript
// VALID - type parameters
type Greeting<Name extends string> = \`Hello, \${Name}\`;
type MyGreeting = Greeting<"World">;  // "Hello, World"

// VALID - union types
type Size = "small" | "medium" | "large";
type SizeClass = \`size-\${Size}\`;  // "size-small" | "size-medium" | "size-large"

// VALID - intrinsic string types
type ShoutGreeting<T extends string> = \`HELLO, \${Uppercase<T>}\`;
type Loud = ShoutGreeting<"world">;  // "HELLO, WORLD"

// VALID - combining multiple unions
type Color = "red" | "blue";
type Shade = "light" | "dark";
type ColorShade = \`\${Shade}-\${Color}\`;
// "light-red" | "light-blue" | "dark-red" | "dark-blue"

// INVALID - property access
type User = { name: string };
type UserName<T> = \`User: \${T["name"]}\`;  // Error!

// INVALID - conditional in template
type Maybe<T> = \`\${T extends string ? T : "unknown"}\`;  // Error!

Stick to type parameters, unions, and the four intrinsic string manipulation utilities.

3Refactor property access to helper types

If you need to access object properties in a template literal, extract them first:

typescript
// WRONG - accessing property inside template
type Event = { type: "click" | "hover" };
type EventHandler = \`on\${Capitalize<Event["type"]>}\`;  // Error!

// CORRECT - extract property first
type Event = { type: "click" | "hover" };
type EventType = Event["type"];  // "click" | "hover"
type EventHandler = \`on\${Capitalize<EventType>}\`;  // "onClick" | "onHover"

// Another example with nested objects
type Config = {
  mode: {
    value: "light" | "dark";
  };
};

// WRONG
type ThemeClass = \`theme-\${Config["mode"]["value"]}\`;  // Error!

// CORRECT
type ModeValue = Config["mode"]["value"];
type ThemeClass = \`theme-\${ModeValue}\`;  // "theme-light" | "theme-dark"

This pattern separates the property access from the template literal construction.

4Use mapped types outside template literals

Mapped types cannot be used inside template literal interpolations. Apply them separately:

typescript
// WRONG - mapped type in template
type Keys = "name" | "age";
type GetterNames = \`get\${Capitalize<{ [K in Keys]: K }[Keys]>}\`;  // Error!

// CORRECT - map first, then template
type Keys = "name" | "age";
type Getters = {
  [K in Keys as \`get\${Capitalize<K>}\`]: () => string;
};
// Result: { getName: () => string; getAge: () => string; }

// Another approach - iterate union directly
type GetterName<K extends string> = \`get\${Capitalize<K>}\`;
type GetterNames = GetterName<Keys>;  // "getName" | "getAge"

Mapped types work well with template literals using the \as\ clause for key remapping, but not inside \${} interpolations.

5Leverage intrinsic string manipulation types

Use TypeScript's built-in string transformation utilities correctly:

typescript
// Available intrinsic types (since TypeScript 4.1)
type Upper = Uppercase<"hello">;      // "HELLO"
type Lower = Lowercase<"WORLD">;      // "world"
type Cap = Capitalize<"typescript">;  // "Typescript"
type Uncap = Uncapitalize<"Hello">;   // "hello"

// Combining them in template literals
type EventName = "click" | "focus" | "blur";
type HandlerName = \`on\${Capitalize<EventName>}\`;
// "onClick" | "onFocus" | "onBlur"

// Chaining transformations
type Method = "getUserData" | "setUserData";
type SnakeCase<T extends string> = Lowercase<T>;
type ApiEndpoint<T extends string> = \`/api/\${SnakeCase<T>}\`;
type Endpoint = ApiEndpoint<Method>;
// "/api/getuserdata" | "/api/setuserdata"

// Complex example - converting camelCase to kebab-case (requires helper)
type KebabCase<S extends string> = S extends \`\${infer A}\${infer B}\`
  ? B extends Uncapitalize<B>
    ? \`\${Lowercase<A>}\${KebabCase<B>}\`
    : \`\${Lowercase<A>}-\${KebabCase<B>}\`
  : S;

type CssClass = KebabCase<"backgroundColor">;  // "background-color"

These four intrinsic types are the only built-in transformations allowed in template literals.

Advanced notes

### Template Literal Type Recursion

Template literal types can be used recursively for complex string parsing and transformation:

\\\typescript // Parse path parameters from route string type ExtractParams<T extends string> = T extends \\${infer Start}:\${infer Param}/\${infer Rest}\ ? Param | ExtractParams<\/\${Rest}\> : T extends \\${infer Start}:\${infer Param}\`
? Param
: never;

type Route = "/users/:userId/posts/:postId";
type Params = ExtractParams<Route>; // "userId" | "postId"
\\\`

Important: Recursive template literal types have depth limits (around 50 levels) to prevent infinite loops. Use them judiciously for parsing tasks.

### Combining with Conditional Types

While you cannot put conditional types inside \${}, you can use them in the broader type definition:

\\\typescript // Pattern: conditional wrapping the entire template literal type Prefix<T extends string> = T extends \_\${infer _}\ ? T // Already has prefix : \_\${T}\`; // Add prefix

type Result1 = Prefix<"test">; // "_test"
type Result2 = Prefix<"_test">; // "_test" (not "__test")
\\\`

### Performance Considerations

Complex template literal types with large unions can significantly slow compilation:

\\\typescript // This creates 1000 × 1000 = 1,000,000 type combinations! type ThousandNumbers = "0" | "1" | "2" /* ... */ | "999"; type Combinations = \\${ThousandNumbers}-\${ThousandNumbers}\; \\\

Best practices:
- Limit union sizes in template literals (keep under 100 members when possible)
- Cache intermediate types to reduce recalculation
- Use simpler patterns when union explosion occurs

### Real-World Use Cases

1. Type-safe CSS-in-JS:
\\\typescript type CSSUnit = "px" | "em" | "rem" | "%"; type CSSValue<T extends number> = \\${T}\${CSSUnit}\`;

type Spacing = CSSValue<4 | 8 | 16 | 32>;
const margin: Spacing = "16px"; // Valid
const padding: Spacing = "20px"; // Error: not in union
\\\`

2. Type-safe event handling:
\\\typescript type DOMEvent = "click" | "focus" | "blur" | "change"; type EventHandler = \on\${Capitalize<DOMEvent>}\; type Props = { [K in EventHandler]?: () => void; }; // Result: { onClick?: () => void; onFocus?: () => void; ... } \\\

3. API route type safety:
\\\typescript type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE"; type Resource = "users" | "posts" | "comments"; type Endpoint = \\${Uppercase<HTTPMethod>} /api/\${Resource}\`;

const route: Endpoint = "GET /api/users"; // Valid
const invalid: Endpoint = "PATCH /api/auth"; // Error
\\\`

### Debugging Template Literal Types

To see what a complex template literal type resolves to:

\\\`typescript
// Hover over 'Result' in your IDE to see the expanded type
type Result = YourComplexTemplateLiteralType;

// Or use a type assertion to check
const test: YourComplexTemplateLiteralType =
"expected-value" as const; // See if it type-checks
\\\`

Use \tsc --explainFiles\ and hover tooltips in VS Code to inspect complex type expansions.

Frequently asked questions

What does "Cannot use expression in template literal type" mean?
The "Cannot use expression in template literal type" error appears when TypeScript encounters an expression that isn't valid within the template literal type syntax. 1, allow you to create new string types by combining and transforming existing string literal types at the type level. However, template literal types have strict limitations on what can be placed inside the ${} interpolation syntax.
How do I fix "Cannot use expression in template literal type"?
This TypeScript error occurs when you attempt to use an invalid expression or operation within a template literal type definition. Template literal types only allow specific type-level constructs like type parameters, unions, and intrinsic string manipulation types, not runtime expressions or complex type operations.
What causes "Cannot use expression in template literal type"?
Using conditional types directly inside template literal type interpolation Attempting to access object properties or array indices within template literals Placing mapped types inside the ${} syntax of template literal types
Is the "Cannot use expression in template literal type" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading