阿里云主机折上折
  • 微信号
Current Site:Index > Primitive types and literal types

Primitive types and literal types

Author:Chuan Chen 阅读数:54764人阅读 分类: TypeScript

Primitive Types

The primitive types in TypeScript are largely consistent with JavaScript, including number, string, boolean, null, undefined, symbol, and bigint. These types represent the most basic, indivisible units of data.

let age: number = 25;
let name: string = "Alice";
let isActive: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;
let uniqueKey: symbol = Symbol("key");
let bigNumber: bigint = 9007199254740991n;

Primitive types are characterized by immutability—once created, they cannot be modified. TypeScript enforces strict type checking for these types:

let price: number = 100;
price = "200"; // Error: Type '"string"' is not assignable to type 'number'

Literal Types

Literal types are a unique feature of TypeScript's type system, allowing values themselves to serve as types. Literal types are divided into string literals, numeric literals, and boolean literals.

String Literal Types

type Direction = "north" | "south" | "east" | "west";

function move(direction: Direction) {
    console.log(`Moving ${direction}`);
}

move("north"); // Correct
move("up");    // Error: Argument of type '"up"' is not assignable to parameter of type 'Direction'

String literal types are often used to define limited sets of strings, such as API endpoints or status codes.

Numeric Literal Types

type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

function rollDice(): DiceRoll {
    return Math.floor(Math.random() * 6) + 1 as DiceRoll;
}

const result: DiceRoll = rollDice();
console.log(`You rolled a ${result}`);

Numeric literal types are suitable for scenarios with fixed numerical ranges, such as dice rolls or HTTP status codes.

Boolean Literal Types

type Yes = true;
type No = false;

function confirmAction(confirmation: Yes | No): void {
    if (confirmation) {
        console.log("Action confirmed");
    } else {
        console.log("Action cancelled");
    }
}

confirmAction(true);  // Correct
confirmAction(false); // Correct
confirmAction(1);     // Error

Boolean literal types are less commonly used but can be useful in scenarios requiring strict true/false distinctions.

Union Types and Literals

Literal types are often combined with union types to create more precise type constraints:

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";

function fetchData(method: HttpMethod, url: string) {
    // Implement fetch logic
}

fetchData("GET", "/api/users");    // Correct
fetchData("OPTIONS", "/api/users"); // Error

This pattern is particularly common in Redux action type definitions:

type Action = 
    | { type: "ADD_TODO"; text: string }
    | { type: "TOGGLE_TODO"; id: number }
    | { type: "DELETE_TODO"; id: number };

function todoReducer(state: Todo[], action: Action): Todo[] {
    switch (action.type) {
        case "ADD_TODO":
            return [...state, { id: Date.now(), text: action.text, completed: false }];
        case "TOGGLE_TODO":
            return state.map(todo => 
                todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
            );
        case "DELETE_TODO":
            return state.filter(todo => todo.id !== action.id);
        default:
            return state;
    }
}

Type Inference and Literals

TypeScript can automatically infer literal types, but sometimes explicit specification is needed:

const name = "Alice";  // Inferred as "Alice" (literal type)
let age = 25;         // Inferred as number

// Explicitly specifying literal types
const direction: "north" = "north";

Using the as const assertion converts object or array properties into literal types:

const colors = ["red", "green", "blue"] as const;
// Type is readonly ["red", "green", "blue"]

const user = {
    name: "Alice",
    age: 25,
    active: true
} as const;
// Type is { readonly name: "Alice"; readonly age: 25; readonly active: true; }

Template Literal Types

TypeScript 4.1 introduced template literal types, enabling the creation of more complex types based on string literals:

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiEndpoint = `/api/${string}`;

function callApi(method: HttpMethod, endpoint: ApiEndpoint) {
    // Implement API call
}

callApi("GET", "/api/users");      // Correct
callApi("POST", "/data/products"); // Error: Does not start with /api/

Template literal types can be combined with conditional types to create powerful type utilities:

type GetterName<T extends string> = `get${Capitalize<T>}`;

type NameGetter = GetterName<"name">;  // "getName"
type AgeGetter = GetterName<"age">;    // "getAge"

function createGetter<T extends string>(property: T): GetterName<T> {
    return `get${property.charAt(0).toUpperCase() + property.slice(1)}` as GetterName<T>;
}

const getName = createGetter("name");  // Type is "getName"
const getAge = createGetter("age");    // Type is "getAge"

Enums and Literal Types

Enums and literal union types can sometimes be used interchangeably, but each has its pros and cons:

// Using enums
enum Direction {
    North = "NORTH",
    South = "SOUTH",
    East = "EAST",
    West = "WEST"
}

// Using literal union types
type Direction = "NORTH" | "SOUTH" | "EAST" | "WEST";

// Enums provide reverse mapping
console.log(Direction.North); // "NORTH"
console.log(Direction["NORTH"]); // "North"

// Literal types are lighter but require manual value maintenance
const directions = {
    North: "NORTH",
    South: "SOUTH",
    East: "EAST",
    West: "WEST"
} as const;

Performance Considerations

Literal types are erased at compile time and do not affect runtime performance. However, extensive use of complex union literal types may increase compilation time:

// Simple literal union - Fast compilation
type SmallUnion = "A" | "B" | "C";

// Complex literal union - May increase compilation time
type LargeUnion = 
    | "A1" | "A2" | "A3" | "A4" | "A5"
    | "B1" | "B2" | "B3" | "B4" | "B5"
    | "C1" | "C2" | "C3" | "C4" | "C5"
    // ...More literals
    | "Z99";

Type Guards and Literals

Type guards are particularly useful when working with literal types:

type Result = { status: "success"; data: string } | { status: "error"; message: string };

function handleResult(result: Result) {
    if (result.status === "success") {
        console.log(result.data);  // Here, result is inferred as { status: "success"; data: string }
    } else {
        console.error(result.message); // Here, result is inferred as { status: "error"; message: string }
    }
}

Application in Configuration Patterns

Literal types are well-suited for defining configuration object types:

type LogLevel = "debug" | "info" | "warn" | "error";

interface LoggerConfig {
    level: LogLevel;
    format: "json" | "text";
    timestamp: boolean;
}

function createLogger(config: LoggerConfig) {
    // Implement logger creation logic
}

createLogger({
    level: "info",
    format: "json",
    timestamp: true
}); // Correct

createLogger({
    level: "verbose", // Error: Not a valid LogLevel
    format: "yaml",   // Error: Not a valid format
    timestamp: "yes"  // Error: Should be boolean
});

Combining with Generics

Literal types can be combined with generics to create flexible APIs:

type EventMap = {
    click: { x: number; y: number };
    keypress: { key: string; code: number };
    scroll: { position: number };
};

function addEventListener<T extends keyof EventMap>(
    event: T,
    handler: (payload: EventMap[T]) => void
) {
    // Implement event listening
}

addEventListener("click", (payload) => {
    console.log(payload.x, payload.y);  // payload is correctly inferred as { x: number; y: number }
});

addEventListener("keypress", (payload) => {
    console.log(payload.key);  // payload is correctly inferred as { key: string; code: number }
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.