TypeScript Literal Types

Literal Types are a feature in TypeScript that represents specific values rather than generic types.

It allows us to restrict the variable type to specific values, rather than broad types like string, number, etc.

This provides more precise type control and makes the code more type-safe.


SVG Diagram: Literal Types Background Title Literal Types Example General Type General Type var direction: string; Can assign any string "up", "down", "abc"... Arrow Literal Type Literal Type var direction: "up" | "down" | "left" | "right" Can only assign one of these four values Arrow Result Type Safety When assigning an incorrect value Compilation Error! direction = "upup" Lower section: Kinds of Literal Types Kinds of Literal Types String literal String literal "up" | "down" | "left" Restrict to specific strings Number literal Number literal 1 | 2 | 3 | 100 | 200 Restrict to specific numbers Boolean literal Boolean literal true | false true or false Template literal Template literal `on${string}` Dynamically generated Arrow marker

Why Need Literal Types

In TypeScript, default types like string and number are too broad.

Sometimes we need more precise type control. For example, a variable can only take a few specific values.

Literal types are exactly designed to solve such problems.

Concept Explanation:Literal types are concrete values of general types (string, number). For example"up"YesstringA subtype, and the only values that can be assigned to it are specific strings.


String Literal Types

String literal types restrict the variable to specific string values.

This is useful when you need to restrict a variable to only take certain fixed values.

Example

// Define a string literal type
// direction can only be one of four specific values
var direction: "up" | "down" | "left" | "right";

// Correct: assign a value from the literal type
direction = "up";

// Error: assign a value not in the list
// direction = "upup"; // Compilation error!

console.log("Direction: " + direction);

// Use a type alias to create a reusable literal type
// Define a status type: can only be one of three fixed values
type Status = "pending" | "active" | "completed";

// Use the type alias
var currentStatus: Status = "active";
console.log("Status: " + currentStatus);

Output:

方向: up
Status: active

Application scenarios:String literals are often used to define enum values, status codes, configuration options, and other scenarios that need to be restricted to specific values.


Number Literal Types

Number literal types restrict the variable to specific numeric values.

This is useful when you need to use specific numeric values (such as status codes, error codes).

Example

// Define a number literal type
// code can only be one of three specific values
var code: 200 | 404 | 500;

// Correct: assign 200
code = 200;

// Error: assign a value not in the list
// code = 301; // Compilation error!

console.log("Status code: " + code);

// Use number literals to simulate an enum
// Define the days of a week
type Weekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;

// Can only assign numbers between 1 and 7
var today: Weekday = 1;
console.log("Today is weekday: " + today);

Explanation:Number literals can be used as a replacement for simple enums, especially when only a few fixed numeric values are needed.


Boolean Literal Types

Boolean literal types restrict the variable to true or false.

In fact, the boolean type in TypeScript is an alias for `true | false`.

Example

// Define a boolean literal type
// isActive can only be true or false
var isActive: true | false;

// Assign true
isActive = true;

// In fact, boolean is a union type of true | false
// So these two ways of writing are equivalent
var flag: boolean = true;

console.log("Is active: " + isActive);

Note:Although boolean itself is sufficient, when you need to clearly distinguish between true and false types, you can use true or false as a type.


Object Literal Types

Object literal types can define the structure of an object, and can use the readonly modifier to make properties read-only.

This is very useful when you need to create immutable objects.

Example

// Define an object literal type
// Specify the object's structure and property types
type Point = {
    // The x-coordinate of the point
    x: number;
    // The y-coordinate of the point
    y: number;
};

// Use the object literal type
var p: Point = { x: 10, y: 20 };
console.log("Point: " + JSON.stringify(p));

// Define a read-only object type
// Use the readonly modifier to make properties read-only
type ReadonlyPoint = {
    // Readonly x-coordinate
    readonly x: number;
    // Readonly y-coordinate
    readonly y: number;
};

// Use the read-only object type
var rp: ReadonlyPoint = { x: 1, y: 2 };

// Attempting to modify a read-only property will cause an error
// rp.x = 3; // Compilation error: read-only property cannot be modified

console.log("Readonly point: " + JSON.stringify(rp));

Output:

点: {"x":10,"y":20}
只读点: {"x":1,"y":2}

Application scenarios:Readonly objects are often used to define configuration objects, constant objects, and other data that should not be modified.


Literal Types and Type Inference

TypeScript automatically infers literal types based on how variables are declared.

Variables declared with const are inferred as specific literal types, rather than broad types.

Example

// Declare and assign using var
// TypeScript will infer a broad type
var name = "Alice";    // Type: string (because var can be reassigned)
var age = 25;          // Type: number
var enabled = true;    // Type: boolean

console.log("Name: " + name + ", Age: " + age + ", Enabled: " + enabled);

// Use as const to create a deep read-only literal
// The array becomes a read-only tuple, and values are literal types
var colors = ["red", "green", "blue"] as const;

// The type of colors becomes:
// readonly ["red", "green", "blue"]

// Access array elements
console.log("Colors: " + colors[0]);

// Attempting to modify will cause an error
// colors

Explanation:as constIt will infer the type as the most specific literal type and add the readonly modifier.


Template Literal Types

Template literal types use JavaScript template string syntax to create new types.

It allows us to dynamically generate string types.

Example

// Use template literal types to define event names
// `on${string}` represents any string starting with "on"
type EventName = `on${string}`;

// Use template literal types to define handler names
// Capitalize converts the first letter of a string to uppercase
type Handler = `handle${Capitalize<string>}`;

// Use the template literal type
var event: EventName = "onClick";
var handler: Handler = "handleSubmit";

console.log("Event: " + event);
console.log("Handler: " + handler);

// Error: does not match template format
// var e: EventName = "click"; // Compile error: does not start with "on"

Run result:

事件: onClick
处理器: handleSubmit

Built-in utility types:Template literal types can be used with built-in utility types such as Uppercase, Lowercase, Capitalize, Uncapitalize, etc.


Real-world Application: Redux Action

Literal types have a wide range of applications in real-world development.

The following shows how to use literal types to implement Redux-style actions.

Example

// Define Redux-style Action types
// Use union types to define different types of Actions
type Action =
    // Action of increment type, containing a numeric payload
    | { type: "increment"; payload: number }
    // Action of decrement type, containing a numeric payload
    | { type: "decrement"; payload: number }
    // Action of reset type, without payload
    | { type: "reset" };

// Reducer function that uses action
function reducer(action: Action): void {
    // TypeScript will automatically infer the specific type of action based on the value of action.type
    switch (action.type) {
        case "increment":
            // In this branch, action is inferred as { type: "increment"; payload: number }
            console.log("Increment: " + action.payload);
            break;
        case "decrement":
            // In this branch, action is inferred as { type: "decrement"; payload: number }
            console.log("Decrement: " + action.payload);
            break;
        case "reset":
            // In this branch, action is inferred as { type: "reset" }
            console.log("Reset");
            break;
    }
}

// Dispatch different types of Actions
reducer({ type: "increment", payload: 5 });
reducer({ type: "reset" });

Run result:

增加: 5
重置

Discriminated union:This pattern is called a "discriminated union", which is the best practice for handling multiple related types in TypeScript.


Notes

  • Type narrowing:When using switch or if checks, TypeScript automatically narrows literal types
  • Type aliases:It is recommended to use type aliases to create aliases for commonly used literal types
  • const inference:Variables declared with const are automatically inferred as literal types
  • as const:Use as const when deep readonly is needed

Best practices:When you need to restrict a variable to only specific values, prefer literal types over enums.


Summary

Literal types are an important feature of TypeScript for achieving precise type control.

  • String literals:Restricted to specific strings, such as"up" | "down"
  • Numeric literals:Restricted to specific numbers, such as1 | 2 | 3
  • Boolean literals:Restricted to true or false
  • Object literals:Define object structure, can use readonly
  • as const:Create deep readonly literal types
  • Template literals:Dynamically generate types using template strings

Advanced:Combining literal types with the discriminated union pattern enables powerful type safety.

Other extensions