Marius Schulz
Marius Schulz
Front End Engineer

String Literal Types in TypeScript

With TypeScript 1.8, string literal types made their way into the language. The pull request in which they're implemented summarizes them as follows:

A string literal type is a type whose expected value is a string with textual contents equal to that of the string literal type.

In other words: A variable of a string literal type can only be assigned the exact string value specified in the string literal type. As with all other types in TypeScript, null and undefined are valid values as well (at least until we get non-nullable types in TypeScript 2.0).

Here are some examples that show how string literal types can be used:

const eventName1: "click" = "click"; // OK
const eventName2: "click" = null; // OK
const eventName3: "click" = undefined; // OK

// Error: Type '"mouseover"' is not assignable to type '"click"'.
const eventName4: "click" = "mouseover";

// Error: Type '""' is not assignable to type '"click"'.
const eventName5: "click" = "";

#String Literal Types and Union Types

A string literal type is not that useful on its own because a variable of that type can only be assigned a single string value. However, things start to get interesting when string literal types are used in conjunction with union types to describe a finite set of possible string values, such as various event types:

type EventType = "click" | "mouseover";

You can now write a function that expects a parameter of type EventType and have the TypeScript compiler check for you that only the values "click" or "mouseover" (or null or undefined, of course) are passed:

function on(event: EventType, callback: () => any) {
  // ...
}

// OK
on("click", () => console.log("Click!"));

// OK
on("mouseover", () => console.log("Mouseover!"));

// Error: Argument of type '"drag"' is not assignable to parameter of type '"click" | "mouseover"'. Type '"drag"' is not assignable to type '"mouseover"'.
on("drag", () => console.log("Drag!"));

In scenarios like this, string literal types help you catch typos in string literals at compile-time rather than at run-time. Also, your IDE tooling can suggest to you all the possible values for a given string literal type. More compile-time safety and smart tooling — after all, that's what TypeScript is all about!

#String Literal Types vs. Strings

Quoting from the pull request that implemented string literal types:

A string literal type can be considered a subtype of the string type. This means that a string literal type is assignable to a plain string, but not vice-versa.

Hence, you can treat a variable that has a string literal type like a variable of type string. You can access properties, call methods, and use operators, just as you would with regular strings:

const eventName: "click" | "mouseover" = "click";

eventName.length; // 5
eventName.toUpperCase(); // "CLICK"
eventName + "!"; // "click!"

Note that the reverse is not true. You cannot assign the type string to a string literal type. A variable of type string can generally contain an arbitrary string value; therefore, the compiler cannot ensure that the value is valid for a given string literal type:

const event: string = "something entirely different";

// Error: Type 'string' is not assignable to type '"click" | "mouseover"'. Type 'string' is not assignable to type '"mouseover"'.
const eventType: "click" | "mouseover" = event;