TypeScript 中的类型断言(Type Assertion),就是告诉编译器:
"我知道这个变量是什么类型,你按照我指定的类型来处理。"
类型断言最常用的语法是:
csharp
变量 as 类型
注意:类型断言只影响编译阶段的类型检查,不会在运行时进行类型转换,也不会校验数据是否符合类型。
1. 最简单的类型断言
typescript
let value: unknown = "hello";
// 断言为 string 类型
const str = value as string;
console.log(str.length); // 5
console.log(str.toUpperCase()); // HELLO
这里:
csharp
value as string
表示告诉 TypeScript,value 是字符串类型。
2. 对象类型断言
假设有一个用户类型:
typescript
interface User {
id: number;
name: string;
age: number;
}
使用类型断言:
typescript
const data: unknown = {
id: 1001,
name: "张三",
age: 25
};
const user = data as User;
console.log(user.name); // 张三
console.log(user.age); // 25
这里的 as User 让编译器按照 User 类型检查 user。
但是如果实际数据缺少属性:
ini
const data: unknown = {
id: 1001,
name: "张三"
};
const user = data as User;
console.log(user.age); // undefined
TypeScript 不会报编译错误,但运行时可能出问题。
3. 联合类型断言
typescript
let value: string | number = "hello";
const str = value as string;
console.log(str.length); // 5
当变量可能有多种类型时,可以使用断言指定其中一种类型。
不过,更推荐使用类型收窄:
typescript
function printValue(value: string | number) {
if (typeof value === "string") {
console.log(value.length);
} else {
console.log(value.toFixed(2));
}
}
因为类型收窄会进行真实的运行时判断,而断言不会。
4. API 返回值类型断言
实际业务开发中非常常见。
typescript
interface Task {
taskId: string;
status: string;
priority: number;
}
async function getTask() {
const response = await fetch("/api/task");
const data = (await response.json()) as Task;
console.log(data.taskId);
console.log(data.status);
}
这里:
arduino
as Task
表示告诉 TypeScript,API 返回的数据符合 Task 类型。
但是如果接口实际返回:
json
{
"taskId": "1001",
"status": "running"
}
缺少 priority,类型断言也不会发现。
5. 双重断言:as unknown as T
当两个类型没有足够的结构重叠时,TypeScript 通常不允许直接断言。
typescript
interface User {
name: string;
}
interface Task {
taskId: number;
}
const user: User = {
name: "张三"
};
// ❌ 类型不兼容
// const task = user as Task;
// ✅ 双重断言可以通过编译
const task = user as unknown as Task;
这里:
typescript
user as unknown as Task
分为两步:
user as unknown:先断言为unknown。as Task:再断言为Task。
双重断言会绕过部分类型安全检查,一般不建议随意使用。
6. 非空断言:!
这是 TypeScript 中另一个常见的断言语法。
php
function getName(name: string | null) {
return name!.toUpperCase();
}
name! 告诉 TypeScript:
我确信 name 不会是 null 或 undefined。
不过,如果传入 null:
scss
getName(null); // 运行时报错
因此非空断言也存在运行时风险。
7. as const 常量断言
csharp
const STATUS = {
PENDING: "pending",
RUNNING: "running",
SUCCESS: "success"
} as const;
使用 as const 后,TypeScript 推断为:
bash
{
readonly PENDING: "pending";
readonly RUNNING: "running";
readonly SUCCESS: "success";
}
这与普通 as Type 不完全一样,它会保留字面量类型,并为对象和数组字面量添加只读约束。
8. as 和 : 的区别(重点)
typescript
interface User {
id: number;
name: string;
}
方式一:类型声明 :
ini
const user: User = {
id: 1
};
// ❌ 报错:缺少 name
编译器要求赋值的数据符合 User 类型。
方式二:类型断言 as
csharp
const user = {
id: 1
} as User;
// ✅ 可以通过编译
即使缺少 name,由于存在部分结构重叠,断言仍可能通过。
9. 总结
| 语法 | 作用 |
|---|---|
value as string |
把变量视为 string 类型 |
data as User |
把对象视为 User 类型 |
value as unknown as T |
双重断言,绕过某些类型兼容限制 |
value! |
非空断言 |
value as const |
字面量常量断言 |
const value: T |
类型声明,不是类型断言 |
实际开发建议:
- 已知变量类型:优先使用
: Type。 - 需要校验某个表达式的类型:考虑
satisfies Type。 - 编译器无法确定,但你确信变量类型:使用
as Type。 - 来自接口或外部的不可信数据:使用运行时校验,而不是仅使用类型断言。
最重要的一句话:类型断言不是类型转换。
例如:
typescript
const value = "123";
const num = value as unknown as number;
console.log(typeof num); // string
虽然 TypeScript 认为 num 是 number,但运行时它依然是字符串。
如果你真正想把字符串转换成数字,应该使用:
javascript
const num = Number(value);
console.log(typeof num); // number