TypeScript - Template Literal Types



Template string literals are used to create dynamic strings with variables in JavaScript. Similarly, in TypeScript you can use template literal types to create the dynamic types, which is introduced in the TypeScript 4.1 version. The syntax of the template literal types in TypeScript is very similar to the JavaScript template string literals.

Syntax

You can follow the syntax below to use the template literal types in TypeScript.

type type_name = `some_text ${variable_name}`
  • In the above syntax, 'type_name' is the name of the type.

  • You need to use the backticks (``) to define template literal types.

  • The 'some_text' can be any string value that will remain constant.

  • To add any dynamic value in the type, you need to add the variable name inside the '${}' structure.

Examples

Let's understand the Template Literal Types in details with the help of some examples in TypeScript.

Example: Basic Use

In the code below, we have defined the 'World' type which contains the “World” as a value. The 'Greeting' type is a template literal type whose value changes based on the value of the 'World' variable.

Next, we created the 'greeting' variable of type 'Greeting' which contains a “Hello World” value. If we try to assign another value to it, the TypeScript compiler throws an error.

type World = "world";
// Defining a template literal type
type Greeting = `Hello, ${World}`;
const greeting: Greeting = "Hello, world"; // Correct
// const wrongGreeting: Greeting = "Hello, everybody"; // Error: This type is "Hello, world" specifically.
console.log(greeting);

On compiling, it will generate the following JavaScript code:

const greeting = "Hello, world"; // Correct
// const wrongGreeting: Greeting = "Hello, everybody"; // Error: This type is "Hello, world" specifically.
console.log(greeting);

The above example code will produce the following output:

Hello, world

Example: Combining with Union Types

In this code, the 'size' type contains the union of multiple type values. The 'ResponseMessage' type is a template literal type whose value changes based on the value of the 'Size' type.

The selectSize() function takes a string of type 'Size' as a parameter, and returns the value of type 'ResponseMessage'.

Next, we call the function by passing 'medium' as a function parameter. If we try to pass any other string value than the 'small', 'medium', and 'large' as a function parameter, it will throw an error.

// Creating a type using literal type
type Size = "small" | "medium" | "large";
// Custom template literal type
type ResponseMessage = `${Size} size selected`;

// Function to select size
function selectSize(size: Size): ResponseMessage {
    return `${size} size selected`;
}

// Call the function
const response: ResponseMessage = selectSize("medium"); // "medium size selected"
console.log(response);

On compiling, it will generate the following JavaScript code:

// Function to select size
function selectSize(size) {
    return `${size} size selected`;
}

// Call the function
const response = selectSize("medium"); // "medium size selected"
console.log(response);

Its output is as follows:

medium size selected

Example: Conditional String Patterns

In this example, we have used the generic types with constraints. Here, 'T extends Status' means, the value of T can be only from the status. The statusMessage type is a combination of the type 'T' and 'status' strings.

The printStatus() function takes the type T as a parameter, which can be any one value from the 'Status' type, and returns the value of type 'statusMessage'.

Next, we have called the printStatus() function by passing the “loading” value which is a part of the 'Status' type.

// Definition of the Status type
type Status = "loading" | "success" | "error";
// T can be any of the values in the Status type
type StatusMessage<T extends Status> = `${T}Status`;

// Function to return a message based on the status
function printStatus<T extends Status>(status: T): StatusMessage<T> {
    return `${status} Status` as StatusMessage<T>;
}

// Call the function with the "loading" status
const loadingMessage = printStatus("Loading"); // "loadingStatus"
console.log(loadingMessage);

On compiling, it will generate the following JavaScript code:

// Function to return a message based on the status
function printStatus(status) {
    return `${status} Status`;
}

// Call the function with the "loading" status
const loadingMessage = printStatus("Loading"); // "loadingStatus"
console.log(loadingMessage);

Its output is as follows:

Loading Status

Example: API Route Generation

The below example demonstrates the real-time practical use of the template literal types.

Then, We have defined the Method type which can have any value from the REST API method type. The Entity type defines the entity objects.

The getRoute() method takes the entity and method of type Entity and Method, respectively as a parameter. It returns the string value of type APIRoute after combining the entity and method values.

// Defining the Method and Entity types
type Method = "get" | "post" | "delete";
type Entity = "user" | "post";

// Defining the ApiRoute type
type ApiRoute = `/${Entity}/${Method}`;

// Defining the getRoute function
function getRoute(entity: Entity, method: Method): ApiRoute {
    return `/${entity}/${method}` as ApiRoute;
}

// Using the getRoute function
const userRoute = getRoute("user", "post"); // "/user/post"
console.log(userRoute);

On compiling, it will generate the following JavaScript code:

// Defining the getRoute function
function getRoute(entity, method) {
    return `/${entity}/${method}`;
}

// Using the getRoute function
const userRoute = getRoute("user", "post"); // "/user/post"
console.log(userRoute);

Its output is as follows:

/user/post

In TypeScript, the template literal type is mainly used when you need to create dynamic types.

Advertisements