TypeScript emits this error when a template literal placeholder resolves to another template literal type (often via `keyof T` or a mapped alias) and the compiler cannot treat it as a flattened string union. You can fix it by expanding the inner union, narrowing the placeholder to primitives, or using helper inference to rebuild the string.
The compiler produces "Template literal type cannot contain template literal" because it has to resolve the placeholder inside ``...`` to a concrete string-like union. When the placeholder itself is still a template literal type (a union built from `keyof T`, `${string}`, or a helper alias), the compiler refuses to nest another template literal and raises the message to avoid cycles or symbol-induced unions. The error forces you to simplify the inner alias so the outer literal only sees plain string/number-like members, which keeps template literal types tractable and compatible with indexed access.
If you built the inner union with another literal, expand it when building the outer literal instead of nesting the alias:
Problematic pattern:
\\\ts
// Inner alias remains a template literal type
type ComponentSize = \\${"small" | "large"}\;
type ComponentClass = \button-\${ComponentSize}\; // Error: Template literal type cannot contain template literal
\\\
Fix: inline the union or limit the alias to primitives so the outer literal sees a string union:
\\\ts
type ComponentClass = \button-\${"small" | "large"}\; // Works
// or give ComponentSize a plain string union
type ComponentSize = "small" | "large";
type ComponentClass = \button-\${ComponentSize}\;
\\\
If the placeholder comes from a generalized helper (often \\\\${keyof T}\\\), restrict it before passing it into another literal:
\\\ts
type KeysOf<T extends object> = \\${Extract<keyof T, string | number | bigint | boolean | null | undefined>}\`;
type PrefixedKeys<T extends object> = \on\${Capitalize<KeysOf<T>>}\; // No nested template literal error
\\\
Using \Extract\ or \Exclude<keyof T, symbol>\` keeps the template literal from hiding symbols, so the compiler can accept the constructed string.
When a helper does multiple concatenations, use a conditional type to infer the inner portion and then reconstruct it:
\\\ts
type FlattenLiteral<T extends string> = T extends \\${infer U}\` ? U : T;
type EventName<T extends string> = \handle\${Capitalize<FlattenLiteral<T>>}\;
// Even if T was itself a template literal type, FlattenLiteral narrows it to the inner union before the outer literal rebuilds it.
\\\
This trick lets the outer template literal operate on \U\`, which is a plain union instead of another template literal type.
If you need both the prefixed value and the original literal, build them from the same union rather than reusing a literal alias:
\\\`ts
type States = "idle" | "active";
type Handler = \\${Capitalize<States>}Handler\;
\\\
Avoid writing \type StatesWithSuffix = \\${States}\; type Handler = \\${StatesWithSuffix}Handler\\ because the compiler sees a nested literal instead of a plain \States\` union.
Template literal types became first-class in TypeScript 4.1 by allowing string concatenation at the type level. Each substitution placeholder must reduce to literal strings or number/boolean/nullish variants so the compiler can enumerate the resulting union statically. Nested template literal types, especially those derived from \keyof\ that still carry \symbol\ keys, are rejected because the compiler cannot guarantee symbol-free unions; see the open suggestion around \Template Literal Types derived from the type keys cannot be used as Indexed Access Types in generic contexts\ (microsoft/TypeScript#59909) for more background on why direct nesting is blocked. Flattening the inner union or exposing the primitive union directly keeps TypeScript's template literal reasoning sound and avoids the diagnostic.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript