Typescript Template String Types

How to narrow down string types using the template string mechanism

What are Template Literal Types

Since Typescript 4.1, you can significantly enhance the typings for your strings. Just to quickly recap, in Typescript you can define so-called “Literal Types”. Instead of just defining a variable as string, you can narrow down the type to a specific set of strings that are allowed.

// Define a mutable string as 'Literal Type'.
let key: "name" | "department" = "name";

// This works, as 'department' is part of the type.
key = "department";
// Won't work, as it's not define in the union type.
key = "company";

// Of course, the same applies for functions.
function getKey(): "name" | "department" {
  return Math.random() <= 0.5 ? "name" : "department";

Starting with Typescript 4.1, this concept has been further developed. By taking “Template Literal Strings” from Javascript as inspiration, you can apply the same syntax for your literal types in Typescript. The main difference is that with “Template Literal Types”, you gain much more flexibility, as the type itself can take parameters for this shape - the concept is similar to generics, but slightly different in that it requires specific values.

// Define a Literal Type
type State = "ready";

// Nice, isn't it? As in Javascript's 
// 'template literal strings', we can
// reuse the same syntax for TS.
// The create type here is now 'Key-ready'.
type Key = `key-${State}`;
// This of course works for union types as well.
// Let's take these non-descriptive types for a 
// quick demo:
type GraphState = "sleep" | "ready" | "error";
type MachineState = "booting" | "running";

// And here's the 'template literal type':
type EntityState = `ES-${GraphState | MachineState}`;

// The possible combinations therefore 
// work out like this:
// type EntityState = 'ES-sleep' 
//  | 'ES-ready'
//  | 'ES-error'
//  | 'ES-booting'
//  | 'ES-running'

There’s even more with Typescript 4.3

As I’m writing this, the release candidate for Typescript 4.3 has been released, which includes further enhancements to Template Literal Types.

The type system is now even more dynamic and can better infer the correct types for you. These latest changes further mature Template String Type.

// This example is taken 1:1 from 
// Typescript's 4.3 announcement.
// Please check out the link in the
// addendum to see all examples!

declare let s: string;
declare function f<T extends string>(x: T): T;

// Previously: string
// Now       : `hello-${string}`
let x2 = f(`hello ${s}`);


As you’ve seen, Typescript is such a powerful language when it comes to defining dynamic types. This is especially true for string-based types. With the latest changes in version 4.3, the type system can better infer the correct types than ever, which makes working with Typescript just a pleasure.