Menu

TypeScript Template Literal Types: Syntax and Examples

Template literal types build string literal types with the same backtick syntax as JavaScript template strings: on${Capitalize<E>}. Learn the syntax, how unions multiply, Uppercase and Capitalize, patterns like ${number}px, mapped type getters, and parsing strings with infer.

This page includes runnable editors - edit, run, and see output instantly.

A template literal type builds string literal types with the same backtick syntax as a JavaScript template string. `on${Capitalize<"click" | "focus">}` is the type "onClick" | "onFocus", computed by the compiler:

Template literal types exist only at compile time. They check string literals and typed values while you write code; they add nothing to the JavaScript output. The handler table above uses Record to require one function per name.

Syntax

Inside the backticks you write literal text and ${...} placeholders. A placeholder holds a type, not a value: a string, number, bigint or boolean literal type, a union of them, or one of the wide types string, number, bigint, boolean, null and undefined.

A placeholder with a wide type like string or number makes a pattern: the type stays as `hello ${string}` and any matching string is accepted. A placeholder with a finite union, like boolean, is expanded into its members.

Unions Multiply

With several unions, the result is every combination:

Three sizes times two tones gives six members. The count grows fast: five placeholders that each hold a union of ten letters would be 100,000 members, and TypeScript refuses with error TS2590: Expression produces a union type that is too complex to represent. Use a wide placeholder like ${string} when you do not need every exact value.

Uppercase, Lowercase, Capitalize, Uncapitalize

Four built-in types change the case of string literal types. They are intrinsic: implemented inside the compiler, not written in TypeScript.

TypeInputResult
Uppercase<S>"hello world""HELLO WORLD"
Lowercase<S>"Content-Type""content-type"
Capitalize<S>"hello world""Hello world"
Uncapitalize<S>"UserName""userName"

They change types only. To build the matching runtime string, you still call toUpperCase() or slice and capitalize yourself, and tell TypeScript the result has the precise type:

The as is needed because toUpperCase() is typed to return plain string. The function's signature is what callers see, so capitalize("report") has the literal type "Report".

String Patterns: ${number}px and Friends

A pattern type accepts every string of a given shape. It is handy for CSS values, ids and keys with a known prefix:

${number} accepts any string that JavaScript reads as a number, which is looser than it looks: "-3px", "1e3px" and "0x10px" all type-check. Treat these patterns as a guard against typos in literals, not as full validation.

Template Literals with Mapped Types

Template literal types are most useful as the as clause of a mapped type, where they generate property names from other property names:

string & K keeps only string keys, since Capitalize does not accept numbers or symbols. Each callback gets its parameter type from the property it watches.

Parsing Strings with infer

In a conditional type, a template literal can match a string and capture parts of it with infer. This extracts the parameter names from a route pattern:

Leave out postId in the call and the compiler reports it as missing.

Template Expressions Widen to string

A template string expression in ordinary code is typed string, even when every part is a literal type. Add as const to keep the template literal type:

Without as const, assigning loose to `log:${Level}` fails, because string could be anything.

Frequently Asked Questions

What are template literal types in TypeScript?

String literal types written with backticks and ${...} placeholders, like JavaScript template strings but at the type level. type Greeting = `hello ${string}` accepts any string starting with hello , and `on${Capitalize<"click">}` is the literal type "onClick".

What happens when you put a union in a template literal type?

The template is expanded for every member, and with several unions you get every combination. `${"sm" | "lg"}-${"red" | "blue"}` is "sm-red" | "sm-blue" | "lg-red" | "lg-blue". Very large combinations fail with error TS2590.

What do Uppercase, Lowercase, Capitalize and Uncapitalize do?

They are built-in types that transform string literal types: Uppercase<"id"> is "ID", Lowercase<"ID"> is "id", Capitalize<"name"> is "Name" and Uncapitalize<"Name"> is "name". They only change types; to change a runtime string you still call toUpperCase() and friends.

Do template literal types validate strings at runtime?

No. Like every TypeScript type they are erased, so they only check string literals and typed values at compile time. A string that arrives at runtime, from JSON or user input, is just string until you check it with your own code.

Why is my template string typed as string instead of a literal type?

A template expression like `on${event}` is widened to string when assigned to a variable. Add as const (`on${event}` as const) or annotate the target type, and TypeScript keeps the template literal type, for example "onclick" | "onfocus".

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED