value as Type 是类型断言:它告诉 TypeScript 把 value 当作 Type 来对待。人们常把它叫作类型转换,但它只是给编译器的一条指令。它会从 JavaScript 输出中擦除,运行时既不转换任何东西,也不检查任何东西。
这是最典型的用法:你对某个值的了解比编译器多(这里是一段 JSON 的结构),于是把它说出来。如果你说错了,没有任何东西会提醒你。下面几节说明这意味着什么,以及什么时候运行时检查是更好的选择。
as 与尖括号语法
同一个断言有两种写法:
const someValue: unknown = "hello";
const a = someValue as string; // as syntax
const b = <string>someValue; // angle-bracket syntax, same meaning
尖括号写法不能用在 .tsx 文件中,因为 <string> 会被当作 JSX 标签。处处都用 as,就永远不会遇到这个问题。断言的优先级较低,所以要在后面继续写表达式时,请用括号包起来:(value as string).length。
断言不会转换值
这一点才是真正导致 bug 的地方。断言改变的是编译器对值的认知,而不是值本身:
编译器认为 asserted 是 number,所以 asserted + 1 作为算术运算通过了类型检查。但在运行时它仍然是字符串 "42",JavaScript 做的是字符串拼接。要改变一个值的类型,就要转换它:Number(x)、String(x)、Boolean(x)、BigInt(x)、new Date(x)。字符串转数字 页面对比了这些转换函数。
| 你想要 | 写法 | 运行时效果 |
|---|---|---|
| 告诉编译器一个你已知的类型 | x as T | 无 |
| 把字符串变成数字 | Number(x)、parseInt(x, 10) | 转换 |
| 把任意值变成字符串 | String(x)、`${x}` | 转换 |
| 先检查类型 | 类型守卫、typeof、instanceof | 检查 |
编译器允许哪些断言
as 并非毫无限制。当一个类型可以赋值给另一个类型时,TypeScript 才允许 x as T:放宽("a" as string、dog as Animal)和收窄(animal as Dog、unknown as User)都可以。如果两个类型完全没有重叠,它就会拒绝:
编译器报告 index.ts(3,11): error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. 这条消息本身就点出了绕过的办法:input as unknown as number。这个双重断言能通过编译,但在运行时和上面的例子错得一模一样。当你觉得需要它的时候,正确的修复通常是做一次转换(Number(input))或者换一个类型。
对于对象,重叠规则很宽松。一个对象字面量只要有部分属性就会被接受,as 就是这样悄悄放过不完整的对象的:
类型注解(const draft: User = { name: "Ada" })或 satisfies User 都会报告缺少 email(TS2741)。只有当你确实打算稍后补全对象时,才在对象字面量上使用 as;更好的做法是直接构造完整的对象。
as const 不一样
as const 看起来像断言,作用却和放宽相反:它让字面量的类型尽可能窄。字符串保持为字面量类型,数组变成只读元组,对象属性变成 readonly。
它是安全的,因为它精确地描述了这个字面量,而不是声称编译器看不到的东西。(isSize 中的 sizes as readonly string[] 是一个放宽断言,同样安全:它让 includes 能接受任意字符串。)更多内容见 字面量类型。
什么时候类型守卫更合适
as 是一种声明,类型守卫是一种检查。在数据从代码外部进入的边界处(JSON、fetch、localStorage、用户输入、消息),这种声明可能是假的,而断言会把边界处一个清晰的错误,变成别处一个令人困惑的错误。
几种看起来相似的工具,大致这样区分:
| 工具 | 编译期检查 | 运行时检查 | 适用场景 |
|---|---|---|---|
类型注解 const x: T = ... | 是,完整检查 | 否 | 值由你自己构造 |
satisfies T | 是,完整检查,并保留推断类型 | 否 | 对象字面量、配置 |
as T | 只检查“类型是否有重叠” | 否 | 你比编译器知道得更多 |
x! | 只去掉 null / undefined | 否 | 你确定值已经设置 |
类型守卫 x is T | 守卫的函数体就是普通代码 | 是 | 来自外部的数据 |
as 仍有两种合理用法:收窄编译器无法追踪的东西(两行之前刚设置的 Map 条目、来自无类型库的值),以及在测试代码中构造不完整的测试数据。让这些断言保持简短,并且紧挨着你能确认声明成立的地方。
常见问题
TypeScript 中 as 有什么作用?
value as Type 是类型断言:它告诉编译器从这里开始把 value 当作 Type。它会从编译后的 JavaScript 中移除,所以既不做转换,也不做运行时检查。如果断言是错的,程序会在之后使用这个错误类型的地方出问题。
在 TypeScript 中如何进行类型转换?
TypeScript 没有运行时类型转换。当你比编译器知道得更多时,用 as(或旧写法 <Type>value)改变静态类型。要真正转换一个值,就调用函数:Number("42")、String(42)、Boolean(x)、new Date(text)。
TypeScript 中的“as unknown as”是什么意思?
这是双重断言。当两个类型完全没有重叠时,TypeScript 会拒绝 x as T(错误 TS2352),而先经过 unknown 就能绕过这个检查,因为任何类型都可以断言为 unknown,unknown 也可以断言为任何类型。它会彻底关闭对这个值的类型检查,所以只在测试代码,或者已经用其他方式确认过类型的代码中使用。
TypeScript 中 as 和尖括号有什么区别?
含义上没有区别:<string>value 和 value as string 是同一个断言。尖括号写法不能用在 .tsx 文件中,因为它和 JSX 冲突,所以大家都用 as。
as 和 satisfies 有什么区别?
as 会覆盖推断出的类型,几乎不做检查(允许缺少属性)。satisfies 会对照类型检查这个值,报告缺少或多余的属性,同时保留精确的推断类型。对象字面量优先用 satisfies,只有在你确实比编译器知道得更多时才用 as。