当前位置: 首页 > news >正文

TS中is关键字详解


TypeScript 中 is 关键字详解


一、is 是什么?

is 是 TypeScript 中的 类型谓词(Type Predicate),用于定义 自定义类型守卫函数
通过 value is Type 语法,明确告诉编译器:当函数返回 true 时,参数一定是特定类型


二、核心语法
// 类型谓词语法
function isTypeName(value: unknown): value is TargetType {
  // 返回布尔值的类型判断逻辑
  return /* 判断条件 */;
}

三、is 的核心价值
场景isis
类型推断无法缩小类型范围明确缩小参数类型范围
代码复用性需重复编写类型断言封装类型判断逻辑
可读性条件逻辑分散集中管理类型验证

四、基础用法示例
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)
工作机制基于运行时检查的条件类型推断强制指定类型
安全性高(依赖实际检查逻辑)低(开发者需自行保证正确性)
适用场景动态类型验证明确知道类型但编译器无法推断
错误风险检查逻辑错误导致类型误判错误断言导致运行时崩溃

七、最佳实践指南
  1. 命名规范

    • 守卫函数名以 is 开头(如 isAdminUser
    • 明确表达验证目标(如 isValidEmail
  2. 实现原则

    • 必须包含完整的类型检查逻辑
    • 避免副作用(纯函数)
    // 错误示例:包含副作用
    function isUserAndLog(value: unknown): value is User {
      console.log(value); // 副作用!
      return isUser(value);
    }
    
  3. 组合使用
    将简单守卫组合成复杂守卫:

    function isNonEmptyString(value: unknown): value is string {
      return isString(value) && value.length > 0;
    }
    
  4. 严格测试
    对守卫函数编写单元测试:

    // 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 文件,集中管理所有自定义类型守卫函数。

相关文章:

  • Day51 | 3. 无重复字符的最长子串、12. 整数转罗马数字、49. 字母异位词分组、73. 矩阵置零
  • Class<?> 和Class<T >有什么区别
  • 设计模式简述(一)设计原则
  • 1.0 软件测试全流程解析:从计划到总结的完整指南
  • C++浅谈转型操作符
  • 看爬山虎学本领 软爬机器人来创新 各种场景能适应
  • @reduxjs/toolkit 报错,解决
  • CF每日5题(1300-1500)
  • M-CTC-T: 面向大规模多语言语音识别的伪标签技术
  • 前后端分离下,Spring Boot 请求从发起到响应的完整执行流程
  • wordpress可视化数据采集Scrapes插件,WP博客网站自动采集发布
  • Python 匿名函数(Lambda函数)
  • kmpmanacher
  • 001 vue
  • 从零开始:在Qt中使用OpenGL绘制指南
  • 前向传播、反向传播
  • PDF处理控件Aspose.PDF教程:在Python、Java 和 C# 中旋转 PDF 文档
  • 无限滚动(Infinite Scroll)页面谷歌不收录!必须改回分页吗?
  • FastAPI依赖注入:链式调用与多级参数传递
  • Data_Socket和UDP_Socket