首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TypeScript系列:第五篇 - 断言&守卫(as、satisfies、is、as const)

TypeScript系列:第五篇 - 断言&守卫(as、satisfies、is、as const)

作者头像
奋飛
发布2025-05-31 16:12:46
发布2025-05-31 16:12:46
28000
代码可运行
举报
文章被收录于专栏:Super 前端Super 前端
运行总次数:0
代码可运行

类型断言(Type Assertions)

类型断言(as),是一种编译时的机制,它不会在运行时检查类型,而是告诉编译器按照指定的类型处理变量。

代码语言:javascript
代码运行次数:0
运行
复制
expr as T

类型断言的前提:exprT 的子类型,或者 Texpr 的子类型。

打破必须兼容的前提:可以通过先断言成 unknown 类型或 any 类型,然后再断言为目标类型。

代码语言:javascript
代码运行次数:0
运行
复制
expr as unknown as T
场景一:类型推断

对于没有类型声明的值,TypeScript 会进行类型推断。

代码语言:javascript
代码运行次数:0
运行
复制
type T = 'ligang' | '李刚';
let myName = 'ligang';

let n: T = myName; // 不能将类型“string”分配给类型“T”

TypeScript 推断变量 myName 的类型是 string,而变量 n 的类型是 ligang' | '李刚'。上述会报错。

此时,可以使用类型断言,告诉编译器此处的值是什么类型。TypeScript 一旦发现存在类型断言,就不再进行类型推断,而是直接采用断言给出的类型。

代码语言:javascript
代码运行次数:0
运行
复制
let n: T = myName as T; // ✔️
场景二:类型转换
代码语言:javascript
代码运行次数:0
运行
复制
const username = document.getElementById("username"); // HTMLElement | null

if (username) {  // 排除null
 username.value; // 类型“HTMLElement”上不存在属性“value”
}

as HTMLInputElement 来告诉 TypeScript 编译器, usernameHTMLInputElement 类型的一个实例;从而可以访问 HTMLInputElement 上特有的属性 value

代码语言:javascript
代码运行次数:0
运行
复制
 (username as HTMLInputElement).value // ✔️

HTMLInputElementHTMLElement 的一个子类,代表了 HTML 中的输入元素。

场景三:unknown 类型

指定 unknown 类型的变量的具体类型。

代码语言:javascript
代码运行次数:0
运行
复制
let value: unknown = "Hello";
let len: number = value.length; // “value”的类型为“未知”

在上面的例子中,类型断言 value as string 告诉编译器将 value 当作字符串处理,从而允许访问 .length 属性。

代码语言:javascript
代码运行次数:0
运行
复制
let len: number = (value as string).length; // ✔️

⚠️ 注意:类型断言可以让错误的代码通过编译,但在运行时可能会报错。

如上述 value 实际值为 nullundefined运行时就会抛出错误。

代码语言:javascript
代码运行次数:0
运行
复制
TypeError: Cannot read properties of null (reading 'length')

类型守卫(Type Guards)

类型守卫(is) 是一种运行时的机制,它们通过检查来确保变量的类型,并根据检查结果改变类型信息。

上一篇:TypeScript系列:第四篇 - typeof 与 keyof 中有提及

缩小联合类型或交叉类型

通过缩小类型,可以确保代码块中安全地使用变量。

代码语言:javascript
代码运行次数:0
运行
复制
function isNumber(x: unknown): x is number {
  return typeof x === 'number';
}

在上述的例子中,isNumber 函数是一个用户自定义的类型守卫。它在运行时检查 value 是否为数值,并返回一个布尔值。

代码语言:javascript
代码运行次数:0
运行
复制
let value: unknown = 123;
if (isNumber(value)) {
  console.log(value.toFixed(2));
}

如果 isNumber 返回 true,则可以安全地调用 .toFixed() 方法。

其他形式

类型守卫有几种形式,包括:

  • typeof 守卫:检查一个变量是否为特定的原始类型;
  • instanceof 守卫:检查一个变量是否为特定类的实例;
  • in 守卫:检查一个变量是否具有特定的属性;
  • 用户自定义类型守卫:使用函数来实现更复杂的类型检查。

⭐ 类型确认(satisfies)

typescript 4.9 中添加了该运算符。

解决问题: 希望确保匹配某些类型,但也希望保留该表达式的最特定类型,以便进行推断。

as 不同,satisfies 不会改变变量的静态类型,因此在编译时不会影响类型检查的结果。

代码语言:javascript
代码运行次数:0
运行
复制
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
const palette: Record<Colors, string | RGB> = {
    red: [255, 0, 0],
    green: "#00ff00",
    blue: [0, 0, 255]
};

const greenNormalized = palette.green.toUpperCase(); // 类型“string | RGB”上不存在属性“toUpperCase”。

上述丢失了每个属性的特定类型,如 green: "#00ff00"string 类型,而通过上述方式变为了 string | RGB

代码语言:javascript
代码运行次数:0
运行
复制
const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
} satisfies Record<Colors, string | RGB>;

satisfies 满足可以验证表达式的类型是否匹配某种类型,而不需要更改该表达式的结果类型

as const 断言

as const 会将字面量的类型断言为不可变类型,缩小成 TypeScript 允许的最小类型。

表达式

说明

let s1 = 'JavaScript'

类型推断为基本类型 string

const s2 = 'JavaScript'

类型推断为值类型:字符串 “JavaScript”

let s3 = 'JavaScript' as const

类型推断为值类型:字符串 “JavaScript” 【等同于上述 s2】

⚠️ as const 断言只能用于字面量,不能用于变量!

作用于对象

as const 断言可以用于整个对象,也可以用于对象的单个属性。

代码语言:javascript
代码运行次数:0
运行
复制
const o1 = {
  a: 1 as const,
  b: 2,
}; // 类型是 { a: 1; b: number; }

const o2 = {
  a: 1,
  b: 2,
} as const; // 类型是 { readonly a: 1; readonly b: 2; }
作用于数组

数组字面量使用 as const 断言后,类型推断就变成了只读元组。

代码语言:javascript
代码运行次数:0
运行
复制
// 类型推断为 number[]
const ary1 = [1, 2, 3];

// 类型推断为 readonly [1, 2, 3]
const ary2 = [1, 2, 3] as const;

示例:

代码语言:javascript
代码运行次数:0
运行
复制
function add(x:number, y:number) {
  return x + y;
}

const num = [1, 2];
const total = add(...num);  // 扩张参数必须具有元组类型或传递给 rest 参数

上面示例中,变量 num 的类型推断为 number[],导致使用扩展运算符...传入函数add()会报错,因为add()只能接受两个参数,而 ...num 并不能保证参数的个数。

  • 对于固定参数个数的函数,如果传入的参数包含扩展运算符,那么扩展运算符只能用于元组。
  • 只有当函数定义使用了 rest 参数,扩展运算符才能用于数组。
代码语言:javascript
代码运行次数:0
运行
复制
const num = [1, 2] as const;
const total = add(...num); 
作用于枚举
代码语言:javascript
代码运行次数:0
运行
复制
enum Foo {
  X,
  Y,
}
let e1 = Foo.X;            // Foo
let e2 = Foo.X as const;   // Foo.X

变量e1的类型被推断为整个 Enum 类型;使用了as const断言以后,变量e2的类型被推断为 Enum 的某个成员,这意味着它不能变更为其他成员。

非空断言

对于那些可能为空的变量(即可能等于undefinednull),TypeScript 提供了非空断言,保证这些变量不会为空,写法是在变量名后面加上感叹号!

代码语言:javascript
代码运行次数:0
运行
复制
x!.toFixed() // x不为空

总结

  • 类型断言 是一种编译时的机制,它不会在运行时检查类型,而是告诉编译器按照指定的类型处理变量。
  • 类型守卫 是一种运行时的机制,它通过检查来确保变量的类型,并根据检查结果改变类型信息。
  • 类型确认 是一种更加灵活的机制,它指定一个对象必须满足某种类型的结构,但不会改变该对象的类型。

在编写 TypeScript 代码时,推荐尽可能使用类型守卫,因为它们提供了运行时的安全性。类型断言应该谨慎使用,只在你完全确定变量类型的情况下使用,以避免运行时错误。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类型断言(Type Assertions)
    • 场景一:类型推断
    • 场景二:类型转换
    • 场景三:unknown 类型
  • 类型守卫(Type Guards)
    • 缩小联合类型或交叉类型
    • 其他形式
  • ⭐ 类型确认(satisfies)
  • as const 断言
    • 作用于对象
    • 作用于数组
    • 作用于枚举
  • 非空断言
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档