阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 原始类型与字面量类型

原始类型与字面量类型

作者:陈川 阅读数:54765人阅读 分类: TypeScript

原始类型

TypeScript中的原始类型与JavaScript基本一致,包括numberstringbooleannullundefinedsymbolbigint。这些类型表示最基本的不可再分的数据单元。

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;

原始类型的特点是不可变性,一旦创建就不能被修改。TypeScript会对这些类型进行严格的类型检查:

let price: number = 100;
price = "200"; // 错误:不能将类型"string"分配给类型"number"

字面量类型

字面量类型是TypeScript特有的类型系统特性,它允许我们将值本身作为类型。字面量类型分为字符串字面量、数字字面量和布尔字面量三种。

字符串字面量类型

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

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

move("north"); // 正确
move("up");    // 错误:类型"up"的参数不能赋给类型"Direction"的参数

字符串字面量类型常用于定义有限的字符串集合,如API端点、状态码等。

数字字面量类型

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}`);

数字字面量类型适用于固定数值范围的场景,如骰子点数、HTTP状态码等。

布尔字面量类型

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);  // 正确
confirmAction(false); // 正确
confirmAction(1);     // 错误

布尔字面量类型虽然使用较少,但在需要严格区分true/false的场景下很有用。

联合类型与字面量

字面量类型常与联合类型结合使用,创建更精确的类型约束:

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

function fetchData(method: HttpMethod, url: string) {
    // 实现fetch逻辑
}

fetchData("GET", "/api/users");    // 正确
fetchData("OPTIONS", "/api/users"); // 错误

这种模式在Redux的action类型定义中特别常见:

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;
    }
}

类型推断与字面量

TypeScript能自动推断字面量类型,但有时需要明确指定:

const name = "Alice";  // 类型推断为"Alice"(字面量类型)
let age = 25;         // 类型推断为number

// 明确指定字面量类型
const direction: "north" = "north";

使用as const断言可以将对象或数组的属性转换为字面量类型:

const colors = ["red", "green", "blue"] as const;
// 类型为 readonly ["red", "green", "blue"]

const user = {
    name: "Alice",
    age: 25,
    active: true
} as const;
// 类型为 { readonly name: "Alice"; readonly age: 25; readonly active: true; }

模板字面量类型

TypeScript 4.1引入了模板字面量类型,可以基于字符串字面量创建更复杂的类型:

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

function callApi(method: HttpMethod, endpoint: ApiEndpoint) {
    // 实现API调用
}

callApi("GET", "/api/users");      // 正确
callApi("POST", "/data/products"); // 错误:不是以/api/开头

模板字面量类型可以与条件类型结合,创建强大的类型工具:

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");  // 类型为"getName"
const getAge = createGetter("age");    // 类型为"getAge"

枚举与字面量类型

枚举和字面量联合类型在某些场景下可以互换,但各有优缺点:

// 使用枚举
enum Direction {
    North = "NORTH",
    South = "SOUTH",
    East = "EAST",
    West = "WEST"
}

// 使用字面量联合类型
type Direction = "NORTH" | "SOUTH" | "EAST" | "WEST";

// 枚举提供反向映射
console.log(Direction.North); // "NORTH"
console.log(Direction["NORTH"]); // "North"

// 字面量类型更轻量,但需要手动维护值
const directions = {
    North: "NORTH",
    South: "SOUTH",
    East: "EAST",
    West: "WEST"
} as const;

性能考虑

字面量类型在编译时会被擦除,不会影响运行时性能。但大量使用复杂的联合字面量类型可能会增加编译时间:

// 简单的字面量联合 - 编译快
type SmallUnion = "A" | "B" | "C";

// 复杂的字面量联合 - 可能增加编译时间
type LargeUnion = 
    | "A1" | "A2" | "A3" | "A4" | "A5"
    | "B1" | "B2" | "B3" | "B4" | "B5"
    | "C1" | "C2" | "C3" | "C4" | "C5"
    // ...更多字面量
    | "Z99";

类型守卫与字面量

在处理字面量类型时,类型守卫特别有用:

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

function handleResult(result: Result) {
    if (result.status === "success") {
        console.log(result.data);  // 这里result被推断为{ status: "success"; data: string }
    } else {
        console.error(result.message); // 这里result被推断为{ status: "error"; message: string }
    }
}

配置模式中的应用

字面量类型非常适合配置对象的类型定义:

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

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

function createLogger(config: LoggerConfig) {
    // 实现logger创建逻辑
}

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

createLogger({
    level: "verbose", // 错误:不是有效的LogLevel
    format: "yaml",   // 错误:不是有效的format
    timestamp: "yes"  // 错误:应为boolean
});

与泛型结合

字面量类型可以与泛型结合,创建灵活的API:

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
) {
    // 实现事件监听
}

addEventListener("click", (payload) => {
    console.log(payload.x, payload.y);  // payload被正确推断为{ x: number; y: number }
});

addEventListener("keypress", (payload) => {
    console.log(payload.key);  // payload被正确推断为{ key: string; code: number }
});

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌