TS中is关键字详解
TypeScript 中 is
关键字详解
一、is
是什么?
is
是 TypeScript 中的 类型谓词(Type Predicate),用于定义 自定义类型守卫函数。
通过 value is Type
语法,明确告诉编译器:当函数返回 true
时,参数一定是特定类型。
二、核心语法
// 类型谓词语法
function isTypeName(value: unknown): value is TargetType {
// 返回布尔值的类型判断逻辑
return /* 判断条件 */;
}
三、is
的核心价值
场景 | 无 is | 有 is |
---|---|---|
类型推断 | 无法缩小类型范围 | 明确缩小参数类型范围 |
代码复用性 | 需重复编写类型断言 | 封装类型判断逻辑 |
可读性 | 条件逻辑分散 | 集中管理类型验证 |
四、基础用法示例
1. 简单类型验证
function isString(value: unknown): value is string {
return typeof value === "string";
}
function process(input: string | number) {
if (isString(input)) {
// 这里 input 被推断为 string
console.log(input.toUpperCase());
} else {
// 这里 input 被推断为 number
console.log(input.toFixed(2));
}
}
2. 对象类型验证
interface User {
id: number;
name: string;
}
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value
);
}
const data: unknown = JSON.parse('{"id":1,"name":"Alice"}');
if (isUser(data)) {
console.log(data.name); // 安全访问 User 属性
}
五、高级用法技巧
1. 联合类型细化
type Success = { status: "success"; data: string };
type Error = { status: "error"; message: string };
function isSuccess(response: Success | Error): response is Success {
return response.status === "success";
}
function handleResponse(response: Success | Error) {
if (isSuccess(response)) {
console.log(response.data); // 识别为 Success 类型
} else {
console.error(response.message); // 识别为 Error 类型
}
}
2. 泛型类型守卫
function isArrayOf<T>(
value: unknown,
check: (item: unknown) => item is T
): value is T[] {
return Array.isArray(value) && value.every(check);
}
// 使用示例
const unknownArray: unknown = [1, 2, 3];
if (isArrayOf(unknownArray, (item): item is number => typeof item === "number")) {
const sum = unknownArray.reduce((a, b) => a + b, 0); // 类型安全操作
}
3. 递归类型检查
type TreeNode = {
value: number;
children?: TreeNode[];
};
function isTreeNode(value: unknown): value is TreeNode {
return (
typeof value === "object" &&
value !== null &&
"value" in value &&
typeof (value as any).value === "number" &&
( !("children" in value) ||
( Array.isArray((value as any).children) &&
(value as any).children.every(isTreeNode) )
);
}
六、与类型断言的对比
特性 | is 类型守卫 | 类型断言 (as ) |
---|---|---|
工作机制 | 基于运行时检查的条件类型推断 | 强制指定类型 |
安全性 | 高(依赖实际检查逻辑) | 低(开发者需自行保证正确性) |
适用场景 | 动态类型验证 | 明确知道类型但编译器无法推断 |
错误风险 | 检查逻辑错误导致类型误判 | 错误断言导致运行时崩溃 |
七、最佳实践指南
-
命名规范
- 守卫函数名以
is
开头(如isAdminUser
) - 明确表达验证目标(如
isValidEmail
)
- 守卫函数名以
-
实现原则
- 必须包含完整的类型检查逻辑
- 避免副作用(纯函数)
// 错误示例:包含副作用 function isUserAndLog(value: unknown): value is User { console.log(value); // 副作用! return isUser(value); }
-
组合使用
将简单守卫组合成复杂守卫:function isNonEmptyString(value: unknown): value is string { return isString(value) && value.length > 0; }
-
严格测试
对守卫函数编写单元测试:// Jest 测试示例 test("isUser correctly identifies valid users", () => { const validUser = { id: 1, name: "Alice" }; expect(isUser(validUser)).toBe(true); const invalidUser = { name: "Bob" }; expect(isUser(invalidUser)).toBe(false); });
八、常见错误与解决方案
1. 不完整的类型检查
// 错误实现:未检查所有必要属性
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "name" in value;
// 缺少对 id 的检查!
}
// 正确实现
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value &&
typeof (value as any).id === "number" &&
typeof (value as any).name === "string"
);
}
2. 误用类型断言
// 危险做法:绕过实际检查
function isUser(value: unknown): value is User {
return (value as User).name !== undefined; // 可能错误!
}
九、实战应用场景
1. API 响应验证
interface ApiResponse<T> {
code: number;
data: T;
}
function isApiResponse<T>(
value: unknown,
checkData: (data: unknown) => data is T
): value is ApiResponse<T> {
return (
typeof value === "object" &&
value !== null &&
"code" in value &&
"data" in value &&
typeof (value as any).code === "number" &&
checkData((value as any).data)
);
}
// 使用示例
const response: unknown = await fetchData();
if (isApiResponse(response, isUser)) {
console.log(response.data.name); // 安全访问
}
2. Redux Action 类型守卫
type Action =
| { type: "ADD_TODO"; text: string }
| { type: "TOGGLE_TODO"; id: number };
function isAddTodoAction(action: Action): action is { type: "ADD_TODO"; text: string } {
return action.type === "ADD_TODO";
}
// 在 reducer 中使用
function todoReducer(state: State, action: Action) {
if (isAddTodoAction(action)) {
// action.text 可安全访问
return [...state, { text: action.text }];
}
// 处理其他 action
}
十、总结要点
- 核心作用:通过自定义逻辑精确控制类型推断
- 核心语法:
value is Type
类型谓词 - 优势:提升代码安全性与可维护性
- 注意:守卫函数的实现必须严格准确
- 适用:处理未知数据(如 API 响应)、管理复杂状态类型
最后建议:在项目中建立一个 type-guards.ts
文件,集中管理所有自定义类型守卫函数。