类型守卫(Type Guard)是 TypeScript 中用于在运行时收窄类型的关键机制。arg is Type 语法允许你定义一个返回布尔值的函数,告诉 TS 编译器:如果函数返回 true,则参数就是指定的类型。这在处理联合类型、判断 API 返回值、过滤数组等场景中非常实用。
// 类型守卫:arg is Type 的用法
// 1. 基础类型守卫:isString
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function print(value: string | number) {
if (isString(value)) {
console.log(value.toUpperCase()); // value 被收窄为 string
} else {
console.log(value.toFixed(2)); // value 被收窄为 number
}
}
// 2. 对象类型守卫:isUser
interface User {
id: number;
name: string;
email: string;
}
interface Admin {
id: number;
name: string;
role: 'admin' | 'superadmin';
permissions: string[];
}
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'email' in obj &&
'id' in obj
);
}
type Person = User | Admin;
function handlePerson(person: Person) {
if (isUser(person)) {
console.log(person.email); // User 类型,可以访问 email
} else {
console.log(person.permissions); // Admin 类型
}
}
// 3. 过滤数组中的 null/undefined
function isNonNullable<T>(value: T): value is NonNullable<typeof value> {
return value !== null && value !== undefined;
}
const arr = [1, null, 2, undefined, 3];
const filtered = arr.filter(isNonNullable); // number[]
// 4. 判断 API 响应是否有效
interface SuccessResponse {
success: true;
data: unknown;
}
interface ErrorResponse {
success: false;
message: string;
code: number;
}
type ApiResponse = SuccessResponse | ErrorResponse;
function isSuccessResponse(res: ApiResponse): res is SuccessResponse {
return res.success === true;
}
arg is Type 是函数的返回类型注解,告诉 TS 编译器:当函数返回 true 时,arg 的类型就是 Type。typeof 是 JS 运行时操作符,只能返回基本类型字符串("string"、"number"等),不能区分对象类型。类型守卫可以自定义任意复杂判断。instanceof 检查原型链,适合 class 实例。类型守卫基于 interface/type,不依赖原型链。array.filter(isNonNullable) 配合类型守卫可以自动收窄数组元素类型,比手动断言更安全。// 实战:处理多种事件类型
interface ClickEvent { type: 'click'; x: number; y: number; }
interface KeyEvent { type: 'keydown'; key: string; }
interface ScrollEvent { type: 'scroll'; scrollTop: number; }
type AppEvent = ClickEvent | KeyEvent | ScrollEvent;
function isClickEvent(e: AppEvent): e is ClickEvent {
return e.type === 'click';
}
function handleEvent(event: AppEvent) {
if (isClickEvent(event)) {
console.log(`点击位置: ${event.x}, ${event.y}`); // ClickEvent
} else if (event.type === 'keydown') {
console.log(`按键: ${event.key}`); // KeyEvent(discriminated union)
} else {
console.log(`滚动位置: ${event.scrollTop}`); // ScrollEvent
}
}