Skip to content

字面量类型检查 (Literal Type Checking)[区分联合体(Discriminated Unions)]

在 TypeScript 中,字面量类型(literal types)允许你将变量的类型限制为特定的值,比如特定的字符串、数字或布尔值。这种类型的精确性增强了代码的安全性和表达力,使得编译器可以在编译时捕捉到更多潜在的错误。

ts
type UserTextEvent = { value: string; target: HTMLInputElement };
type UserMouseEvent = { value: number; target: HTMLButtonElement };

type userEvent = UserTextEvent | UserMouseEvent;
/*
type userEvent = {
    value: string | number;
    target: HTMLInputElement | HTMLButtonElement;
}
// 确定value 类型后,无法判断target 类型
*/

function handle(event: userEvent) {
  if (typeof event.value === "string") {
    console.log(event.value); // string
    console.log(event.target); // 未确定target 类型:HTMLInputElement | HTMLButtonElement
  } else {
    console.log(event.value);
    console.log(event.target);
  }
}

/**
 * 通过子面量确定是_UserTextEvent 还是_UserMouseEvent
 */

type _UserTextEvent = {
  type: "textEvent";
  value: string;
  target: HTMLInputElement;
};
type _UserMouseEvent = {
  type: "mouseEvent";
  value: number;
  target: HTMLButtonElement;
};

type _userEvent = _UserTextEvent | _UserMouseEvent;
function _handle(event: _userEvent) {
  if (event.type === "textEvent") {
    console.log(event.value); // string
    console.log(event.target); // 确定target 类型:HTMLInputElement | HTMLButtonElement
  } else {
    console.log(event.value);
    console.log(event.target);
  }
}

定义字面量类型

你可以使用具体的字面量值来定义类型,这包括字符串字面量类型、数字字面量类型和布尔字面量类型等。下面是一些例子:

typescript
// 字符串字面量类型
type Easing = "ease-in" | "ease-out" | "ease-in-out";

// 数字字面量类型
type Quantity = 50 | 100;

// 布尔字面量类型
type BooleanLiteral = true | false;

通过这种方式,你可以确保变量只能被赋予这些预定义的值之一。

使用字面量类型

当你声明一个具有字面量类型的变量时,TypeScript 编译器会强制要求该变量的值必须是其中一个字面量值。例如:

typescript
let myEasing: Easing;
myEasing = "ease-in"; // OK
myEasing = "some-other-value"; // Error: Type '"some-other-value"' is not assignable to type 'Easing'.

字面量类型与联合类型

字面量类型经常与联合类型一起使用,以创建一组有限的可能值。这使得你可以定义非常具体的类型,从而提高代码的安全性和可读性。

typescript
function setCssTransition(
  element: HTMLElement,
  property: string,
  duration: number,
  easing: Easing
) {
  element.style.transition = `${property} ${duration}ms ${easing}`;
}

setCssTransition(document.body, "opacity", 200, "ease-in"); // OK
setCssTransition(document.body, "opacity", 200, "linear"); // Error: Argument of type '"linear"' is not assignable to parameter of type 'Easing'.

在这个例子中,setCssTransition 函数接受一个 Easing 类型的参数,它只能是 "ease-in""ease-out""ease-in-out" 之一。

字面量类型与控制流分析

结合 TypeScript 的控制流分析功能,字面量类型可以进一步增强类型保护的效果。例如,你可以根据字面量值的不同执行不同的逻辑分支:

typescript
function getPadding(value: string, padding: "left" | "right") {
  if (padding === "left") {
    return `Left padding: ${value}`;
  } else {
    return `Right padding: ${value}`;
  }
}

在这个例子中,getPadding 函数的 padding 参数只能是 "left""right"。通过条件语句,TypeScript 编译器能够准确地推断出在每个分支中 padding 的具体值,并允许你安全地执行相应的操作。

字面量类型与自定义类型守卫

字面量类型也可以与自定义类型守卫结合使用,以实现更复杂的类型缩小:

typescript
interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

function isCircle(shape: Shape): shape is Circle {
  return shape.kind === "circle";
}

function getArea(shape: Shape) {
  if (isCircle(shape)) {
    return Math.PI * shape.radius ** 2; // shape is Circle here
  } else {
    return shape.sideLength ** 2; // shape is Square here
  }
}

在这个例子中,kind 属性是一个字面量类型,它用于区分不同的形状。通过 isCircle 自定义类型守卫,我们可以有效地缩小 shape 的类型范围,使代码更加安全和易于理解。

注意事项

  • 严格模式:确保你的项目启用了严格的编译选项(如 strict),以便充分利用字面量类型带来的好处。

  • 性能考虑:虽然字面量类型提高了类型安全性,但在某些情况下可能会引入额外的编译开销。不过,通常这种影响是可以忽略不计的。

  • 灵活性:虽然字面量类型提供了强大的类型约束,但也要注意不要过度使用它们,以免限制代码的灵活性。

总结

字面量类型是 TypeScript 提供的一种强大工具,它允许你将变量的类型限制为特定的值,从而提高代码的安全性和表达力。结合联合类型、控制流分析和自定义类型守卫,字面量类型可以帮助你在编写代码时充分利用 TypeScript 的静态类型检查能力。

Released under the MIT License.