用户工具

站点工具


typescript:第四章枚举与字面量类型

第四章:枚举与字面量类型

本章概述

枚举(Enum)和字面量类型(Literal Types)是 TypeScript 提供的强大类型工具。它们可以让代码更具可读性和类型安全性。本章将深入讲解这些类型的定义、使用和最佳实践。

4.1 枚举(Enum)详解

枚举是一种为一组数值赋予友好名称的方式,提高代码的可读性和可维护性。

4.1.1 数字枚举

```typescript 自动递增的数字枚举 enum Direction { Up, 0

Down,    // 1
Left,    // 2
Right    // 3

}

访问枚举成员 console.log(Direction.Up); 0 console.log(Direction.Down); 1 反向映射 console.log(Direction[0]); “Up” console.log(Direction[1]); “Down”

指定起始值 enum Direction2 { Up = 1, 1

Down,      // 2
Left,      // 3
Right      // 4

}

完全指定值 enum StatusCode { OK = 200, NotFound = 404, ServerError = 500 } 混合使用 enum Mixed {

A = 1,
B,      // 2
C = 10,
D       // 11

} ```

4.1.2 字符串枚举

字符串枚举没有反向映射,每个成员必须显式初始化。

```typescript enum Color {

Red = "RED",
Green = "GREEN",
Blue = "BLUE"

}

使用 const myColor: Color = Color.Red; console.log(myColor); “RED”

没有反向映射 console.log(Color[“RED”]); Error 字符串枚举在模板字符串中很有用 function getColorMessage(color: Color): string {

return `The selected color is ${color}`;

}

使用场景:HTTP 方法 enum HttpMethod { Get = “GET”, Post = “POST”, Put = “PUT”, Delete = “DELETE”, Patch = “PATCH” } function request(url: string, method: HttpMethod): void { console.log(`${method} ${url}`); } request(“/api/users”, HttpMethod.Get); ``` ==== 4.1.3 异构枚举 ==== 异构枚举混合了字符串和数字成员(不推荐,但在某些场景有用): ```typescript enum MixedEnum { No = 0, Yes = “YES”, Maybe = 2 } 使用场景:布尔转换 enum BooleanLikeEnum {

No = 0,
Yes = "YES"

} ```

4.1.4 常量枚举(Const Enums)

使用 const 修饰的枚举在编译阶段完全删除,替换为内联常量。

```typescript const enum Direction {

Up = 1,
Down = 2,
Left = 3,
Right = 4

}

使用 const dir = Direction.Up; 编译后的 JavaScript(没有枚举定义): const dir = 1; ``` ==== 4.1.5 枚举编译后的代码 ==== ```typescript TypeScript enum Direction {

Up = 1,
Down,
Left,
Right

} ```

编译为 JavaScript:

```javascript var Direction; (function (Direction) {

  Direction[Direction["Up"] = 1] = "Up";
  Direction[Direction["Down"] = 2] = "Down";
  Direction[Direction["Left"] = 3] = "Left";
  Direction[Direction["Right"] = 4] = "Right";

})(Direction || (Direction = {})); ```

结果是一个同时支持正向和反向查找的对象:

```javascript Direction = {

1: "Up",
2: "Down",
3: "Left",
4: "Right",
Up: 1,
Down: 2,
Left: 3,
Right: 4

} ```

4.2 字面量类型(Literal Types)

字面量类型允许将具体的值作为类型使用。

4.2.1 字符串字面量类型

```typescript 定义字符串字面量类型 type Easing = “ease-in” | “ease-out” | “ease-in-out”; 使用 let animationEasing: Easing = “ease-in”; animationEasing = “linear”; Error: Type '“linear”' is not assignable

使用场景:事件名称 type MouseEventType = “click” | “dblclick” | “mousedown” | “mouseup” | “mousemove”; function handleMouseEvent(eventType: MouseEventType, handler: () ⇒ void): void { document.addEventListener(eventType, handler); } handleMouseEvent(“click”, () ⇒ console.log(“Clicked!”)); handleMouseEvent(“scroll”, () ⇒ {}); Error ``` ==== 4.2.2 数字字面量类型 ==== ```typescript 定义数字字面量类型 type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6; type HttpStatus = 200 | 404 | 500; type Port = 80 | 443 | 3000 | 8080;

使用 function rollDice(): DiceRoll { return (Math.floor(Math.random() * 6) + 1) as DiceRoll; } 使用场景:API 版本 type ApiVersion = 1 | 2 | 3;

function callApi(version: ApiVersion): void {

console.log(`Calling API v${version}`);

} ```

4.2.3 布尔字面量类型

```typescript 布尔字面量类型通常用于对象属性的精确控制 interface ValidationResult { valid: true; 只能是 true

message: string;

}

interface InvalidResult {

valid: false;  // 只能是 false
errors: string[];

}

type Result = ValidationResult | InvalidResult;

function processResult(result: Result): void {

if (result.valid) {
  console.log(result.message);  // TypeScript 知道这是 ValidationResult
} else {
  console.log(result.errors);   // TypeScript 知道这是 InvalidResult
}

} ```

4.3 联合类型(Union Types)

联合类型表示一个值可以是几种类型之一。

4.3.1 基本联合类型

```typescript 基本类型的联合 let value: string | number; value = “hello”; OK value = 42; OK value = true; Error 多个类型的联合 type ID = string | number; type Status = “pending” | “success” | “error”;

复杂类型的联合 interface Square { kind: “square”; size: number; } interface Rectangle { kind: “rectangle”; width: number; height: number; } interface Circle { kind: “circle”; radius: number; } type Shape = Square | Rectangle | Circle; ``` ==== 4.3.2 类型收窄(Narrowing) ==== ```typescript function formatValue(value: string | number): string { 类型守卫

if (typeof value === "string") {
  // TypeScript 知道这里是 string
  return value.toUpperCase();
} else {
  // TypeScript 知道这里是 number
  return value.toFixed(2);
}

}

使用类型谓词 function isString(value: unknown): value is string { return typeof value === “string”; } function process(value: unknown): void { if (isString(value)) { console.log(value.length); OK

}

} ```

4.3.3 可辨识联合(Discriminated Unions)

```typescript interface Circle {

kind: "circle";
radius: number;

}

interface Square {

kind: "square";
side: number;

}

interface Triangle {

kind: "triangle";
base: number;
height: number;

}

type Shape = Circle | Square | Triangle;

function getArea(shape: Shape): number {

switch (shape.kind) {
  case "circle":
    return Math.PI * shape.radius ** 2;
  case "square":
    return shape.side ** 2;
  case "triangle":
    return (shape.base * shape.height) / 2;
  default:
    // 穷举检查
    const _exhaustiveCheck: never = shape;
    return _exhaustiveCheck;
}

} ```

4.4 类型别名(Type Aliases)

类型别名为类型创建新名称,使用 type 关键字。

4.4.1 基本类型别名

```typescript 基本类型别名 type UserID = string; type Age = number; type IsActive = boolean; 使用 const userId: UserID = “user-123”; const age: Age = 25;

对象类型别名 type Point = { x: number; y: number; }; type User = { id: UserID; name: string; age: Age; isActive: IsActive; }; ``` ==== 4.4.2 联合类型别名 ==== ```typescript 联合类型别名 type Status = “pending” | “success” | “error”; type ID = string | number; type Primitive = string | number | boolean | null | undefined;

复杂联合 type Event = | { type: “click”; x: number; y: number } | { type: “keypress”; key: string } | { type: “scroll”; delta: number }; ``` ==== 4.4.3 函数类型别名 ==== ```typescript 函数类型别名 type Callback = (error: Error | null, result: string) ⇒ void; type Predicate<T> = (item: T) ⇒ boolean; type Transformer<T, U> = (input: T) ⇒ U;

使用 type CompareFunction<T> = (a: T, b: T) ⇒ number; const numberCompare: CompareFunction<number> = (a, b) ⇒ a - b; const stringCompare: CompareFunction<string> = (a, b) ⇒ a.localeCompare(b); ``` ===== 4.5 枚举 vs 联合类型 ===== | 特性 | 枚举(Enum) | 联合类型(Union) | |——|————–|——————-| | 编译后代码 | 生成对象 | 完全擦除 | | 反向映射 | 支持(数字枚举) | 不支持 | | 扩展性 | 可以添加方法 | 纯粹类型 | | 树摇优化 | 可能被保留 | 完全移除 | | 常量值 | 可以 | 可以(as const) | | 运行时访问 | 可以 | 不可以 | ==== 4.5.1 何时使用枚举 ==== ```typescript 1. 需要运行时访问 enum LogLevel {

Debug = 0,
Info = 1,
Warn = 2,
Error = 3

}

function log(message: string, level: LogLevel): void {

if (level >= LogLevel.Warn) {  // 运行时比较
  console.error(message);
}

}

2. 需要反向映射 function getLogLevelName(level: number): string { return LogLevel[level] || “Unknown”; } 3. 需要稳定的标识符 enum Permission {

Read = 1 << 0,    // 1
Write = 1 << 1,   // 2
Execute = 1 << 2  // 4

}

const userPermission = Permission.Read | Permission.Write; ```

4.5.2 何时使用联合类型

```typescript 1. 纯类型安全,不需要运行时值 type Status = “loading” | “success” | “error”; 2. 需要复杂的类型组合 type Response<T> =

| { status: "success"; data: T }
| { status: "error"; error: Error };

3. 需要类型收窄 type Event = | { type: “click”; x: number; y: number } | { type: “keypress”; key: string }; function handle(event: Event): void { if (event.type === “click”) { console.log(event.x, event.y); TypeScript 知道是 click 事件

}

}

4. 使用 as const 替代常量枚举 const Colors = { Red: “RED”, Green: “GREEN”, Blue: “BLUE” } as const; type Color = typeof Colors[keyof typeof Colors]; “RED” | “GREEN” | “BLUE” ```

4.6 高级枚举模式

4.6.1 带方法的枚举

```typescript enum HttpStatus {

OK = 200,
NotFound = 404,
ServerError = 500

}

namespace HttpStatus {

export function isSuccess(status: HttpStatus): boolean {
  return status >= 200 && status < 300;
}

export function getMessage(status: HttpStatus): string {
  switch (status) {
    case HttpStatus.OK:
      return "Success";
    case HttpStatus.NotFound:
      return "Resource not found";
    case HttpStatus.ServerError:
      return "Internal server error";
    default:
      return "Unknown status";
  }
}

}

使用 console.log(HttpStatus.isSuccess(HttpStatus.OK)); true console.log(HttpStatus.getMessage(HttpStatus.NotFound)); “Resource not found” ``` ==== 4.6.2 位标志枚举 ==== ```typescript enum Permission { None = 0, Read = 1 « 0, 1

Write = 1 << 1,     // 2
Execute = 1 << 2,   // 4
Delete = 1 << 3     // 8

}

namespace Permission {

export function has(permissions: Permission, permission: Permission): boolean {
  return (permissions & permission) === permission;
}

export function add(permissions: Permission, permission: Permission): Permission {
  return permissions | permission;
}

export function remove(permissions: Permission, permission: Permission): Permission {
  return permissions & ~permission;
}

}

使用 let userPermission = Permission.Read | Permission.Write; console.log(Permission.has(userPermission, Permission.Read)); true console.log(Permission.has(userPermission, Permission.Execute)); false userPermission = Permission.add(userPermission, Permission.Execute); console.log(Permission.has(userPermission, Permission.Execute)); true ```

4.7 常量断言(Const Assertions)

as const 是创建字面量类型的强大工具。

4.7.1 基本用法

```typescript 不使用 as const const config = { apiUrl: “https://api.example.com”, timeout: 5000 }; 类型:{ apiUrl: string; timeout: number }

使用 as const const config2 = { apiUrl: “https://api.example.com”, timeout: 5000 } as const; 类型:{ readonly apiUrl: “https://api.example.com”; readonly timeout: 5000 }

整个对象变为只读 config2.timeout = 3000; Error 数组使用 as const const roles = [“admin”, “user”, “guest”] as const; 类型:readonly [“admin”, “user”, “guest”] type Role = typeof roles[number]; “admin” | “user” | “guest” ```

4.7.2 替代枚举

```typescript 常量对象替代字符串枚举 const Direction = { Up: “UP”, Down: “DOWN”, Left: “LEFT”, Right: “RIGHT” } as const; type Direction = typeof Direction[keyof typeof Direction]; type Direction = “UP” | “DOWN” | “LEFT” | “RIGHT”

使用 const move = (direction: Direction): void ⇒ { console.log(`Moving ${direction}`); }; move(Direction.Up); OK move(“UP”); OK move(“FORWARD”); Error ```

4.8 实用类型模式

4.8.1 状态机模式

```typescript type State =

| { type: "idle" }
| { type: "loading"; progress: number }
| { type: "success"; data: unknown }
| { type: "error"; error: Error };

class StateMachine {

private state: State = { type: "idle" };

transition(newState: State): void {
  // 验证状态转换
  const validTransitions: Record<State["type"], State["type"][]> = {
    idle: ["loading"],
    loading: ["success", "error", "idle"],
    success: ["idle"],
    error: ["idle", "loading"]
  };
  
  const valid = validTransitions[this.state.type].includes(newState.type);
  if (!valid) {
    throw new Error(`Invalid transition from ${this.state.type} to ${newState.type}`);
  }
  
  this.state = newState;
}

getCurrentState(): State {
  return this.state;
}

} ```

4.8.2 配置对象模式

```typescript const Config = {

environments: {
  development: { apiUrl: "http://localhost:3000", debug: true },
  staging: { apiUrl: "https://staging.api.com", debug: true },
  production: { apiUrl: "https://api.example.com", debug: false }
}

} as const;

type Environment = keyof typeof Config.environments; type ConfigType = typeof Config.environments[Environment];

function getConfig(env: Environment): ConfigType {

return Config.environments[env];

}

使用 const devConfig = getConfig(“development”); console.log(devConfig.apiUrl); http://localhost:3000” ```

4.9 本章小结

本章我们学习了:

1. 枚举(Enum) - 数字枚举、字符串枚举、常量枚举、异构枚举 2. 字面量类型 - 字符串、数字、布尔字面量类型 3. 联合类型 - 基本联合、类型收窄、可辨识联合 4. 类型别名 - 为复杂类型创建名称 5. 枚举 vs 联合 - 选择合适的工具 6. 常量断言 - 使用 as const 创建精确类型

4.10 练习题

练习 1:枚举设计

设计一个完整的 HTTP 状态码枚举,包含:

  1. 信息响应 (100-199)
  2. 成功响应 (200-299)
  3. 重定向 (300-399)
  4. 客户端错误 (400-499)
  5. 服务器错误 (500-599)

并添加方法判断状态码类型。

<details> <summary>参考答案</summary>

```typescript enum HttpStatusCode {

// 信息响应
Continue = 100,
SwitchingProtocols = 101,

// 成功响应
OK = 200,
Created = 201,
Accepted = 202,
NoContent = 204,

// 重定向
MovedPermanently = 301,
Found = 302,
NotModified = 304,

// 客户端错误
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404,
MethodNotAllowed = 405,

// 服务器错误
InternalServerError = 500,
NotImplemented = 501,
BadGateway = 502,
ServiceUnavailable = 503

}

namespace HttpStatusCode {

export function isInformational(status: HttpStatusCode): boolean {
  return status >= 100 && status < 200;
}

export function isSuccess(status: HttpStatusCode): boolean {
  return status >= 200 && status < 300;
}

export function isRedirect(status: HttpStatusCode): boolean {
  return status >= 300 && status < 400;
}

export function isClientError(status: HttpStatusCode): boolean {
  return status >= 400 && status < 500;
}

export function isServerError(status: HttpStatusCode): boolean {
  return status >= 500 && status < 600;
}

export function isError(status: HttpStatusCode): boolean {
  return status >= 400;
}

}

使用 console.log(HttpStatusCode.isSuccess(HttpStatusCode.OK)); true console.log(HttpStatusCode.isClientError(HttpStatusCode.NotFound)); true ``` </details> ==== 练习 2:可辨识联合 ==== 实现一个完整的 Redux Action 类型系统: ```typescript 实现以下 Action 类型: - ADD_TODO: { type: “ADD_TODO”, payload: { text: string } } - TOGGLE_TODO: { type: “TOGGLE_TODO”, payload: { id: number } } - DELETE_TODO: { type: “DELETE_TODO”, payload: { id: number } } - SET_FILTER: { type: “SET_FILTER”, payload: { filter: “all” | “active” | “completed” } }

1. 定义 Action 类型 2. 实现 reducer 函数处理所有 action 3. 确保类型安全 ``` <details> <summary>参考答案</summary> ```typescript Action 类型定义 type AddTodoAction = {

type: "ADD_TODO";
payload: { text: string };

};

type ToggleTodoAction = {

type: "TOGGLE_TODO";
payload: { id: number };

};

type DeleteTodoAction = {

type: "DELETE_TODO";
payload: { id: number };

};

type SetFilterAction = {

type: "SET_FILTER";
payload: { filter: "all" | "active" | "completed" };

};

type TodoAction =

| AddTodoAction 
| ToggleTodoAction 
| DeleteTodoAction 
| SetFilterAction;

State 类型 interface Todo { id: number; text: string; completed: boolean; } interface TodoState { todos: Todo[]; filter: “all” | “active” | “completed”; } Reducer function todoReducer(state: TodoState, action: TodoAction): TodoState {

switch (action.type) {
  case "ADD_TODO":
    return {
      ...state,
      todos: [
        ...state.todos,
        {
          id: Date.now(),
          text: action.payload.text,
          completed: false
        }
      ]
    };
    
  case "TOGGLE_TODO":
    return {
      ...state,
      todos: state.todos.map(todo =>
        todo.id === action.payload.id
          ? { ...todo, completed: !todo.completed }
          : todo
      )
    };
    
  case "DELETE_TODO":
    return {
      ...state,
      todos: state.todos.filter(todo => todo.id !== action.payload.id)
    };
    
  case "SET_FILTER":
    return {
      ...state,
      filter: action.payload.filter
    };
    
  default:
    // 穷举检查
    const _exhaustiveCheck: never = action;
    return _exhaustiveCheck;
}

} ```

</details>

练习 3:常量断言实践

使用 as const 实现一个完整的主题配置系统:

```typescript 要求: 1. 定义颜色主题配置,包含 primary、secondary、background 等颜色 2. 定义字体配置 3. 定义间距配置 4. 确保所有配置都是字面量类型和只读的 5. 导出配置的类型 ```

<details> <summary>参考答案</summary>

```typescript const theme = {

colors: {
  primary: "#007bff",
  secondary: "#6c757d",
  success: "#28a745",
  danger: "#dc3545",
  warning: "#ffc107",
  info: "#17a2b8",
  light: "#f8f9fa",
  dark: "#343a40",
  background: "#ffffff",
  text: "#212529"
},
fonts: {
  family: {
    sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
    mono: '"SF Mono", Monaco, monospace'
  },
  size: {
    xs: "0.75rem",
    sm: "0.875rem",
    base: "1rem",
    lg: "1.125rem",
    xl: "1.25rem",
    "2xl": "1.5rem",
    "3xl": "1.875rem"
  }
},
spacing: {
  xs: "0.25rem",
  sm: "0.5rem",
  md: "1rem",
  lg: "1.5rem",
  xl: "2rem",
  "2xl": "3rem"
},
borderRadius: {
  none: "0",
  sm: "0.125rem",
  base: "0.25rem",
  md: "0.375rem",
  lg: "0.5rem",
  full: "9999px"
}

} as const;

导出类型 type Theme = typeof theme; type Colors = typeof theme.colors; type ColorName = keyof Colors; type FontSize = keyof typeof theme.fonts.size; type Spacing = keyof typeof theme.spacing; 使用 function getColor(name: ColorName): string {

return theme.colors[name];

}

function getSpacing(size: Spacing): string {

return theme.spacing[size];

}

使用示例 const primaryColor = getColor(“primary”); “#007bff” const padding = getSpacing(“md”); “1rem” ``` </details> ==== 练习 4:类型收窄应用 ==== 实现一个类型安全的 API 响应处理器: ```typescript 要求: 1. 定义 Loading、Success、Error 三种状态 2. 实现 isLoading、isSuccess、isError 类型守卫函数 3. 实现 handleResponse 函数,根据状态正确处理 interface LoadingState { /* … */ } interface SuccessState<T> { /* … */ } interface ErrorState { /* … */ } type ApiState<T> = LoadingState | SuccessState<T> | ErrorState; 实现类型守卫 function isLoading<T>(state: ApiState<T>): state is LoadingState { /* … */ } function isSuccess<T>(state: ApiState<T>): state is SuccessState<T> { /* … */ } function isError<T>(state: ApiState<T>): state is ErrorState { /* … */ }

实现处理器 function handleResponse<T>(state: ApiState<T>): void { /* … */ } ``` <details> <summary>参考答案</summary> ```typescript 状态定义 interface LoadingState {

status: "loading";

}

interface SuccessState<T> {

status: "success";
data: T;
timestamp: Date;

}

interface ErrorState {

status: "error";
error: Error;
code: number;

}

type ApiState<T> = LoadingState | SuccessState<T> | ErrorState;

类型守卫函数 function isLoading<T>(state: ApiState<T>): state is LoadingState { return state.status === “loading”; } function isSuccess<T>(state: ApiState<T>): state is SuccessState<T> { return state.status === “success”; } function isError<T>(state: ApiState<T>): state is ErrorState { return state.status === “error”; } 响应处理器 function handleResponse<T>(state: ApiState<T>): void {

if (isLoading(state)) {
  console.log("Loading...");
  showSpinner();
} else if (isSuccess(state)) {
  console.log("Success!", state.data);
  console.log("Fetched at:", state.timestamp);
  renderData(state.data);
} else if (isError(state)) {
  console.error("Error:", state.error.message);
  console.error("Code:", state.code);
  showError(state.error);
}

}

辅助函数(模拟) function showSpinner(): void {} function renderData<T>(data: T): void {} function showError(error: Error): void {} 使用示例 const loadingState: ApiState<string> = { status: “loading” }; const successState: ApiState<string> = {

status: "success", 
data: "Hello", 
timestamp: new Date() 

}; const errorState: ApiState<string> = {

status: "error", 
error: new Error("Network error"), 
code: 500 

};

handleResponse(loadingState); handleResponse(successState); handleResponse(errorState); ```

</details>

扩展阅读

typescript/第四章枚举与字面量类型.txt · 最后更改: 127.0.0.1