TypeScript与RxJS进阶面试:类型体操、流式编程、错误处理、考察点

本章聚焦 Angular 跨端开发中最核心的进阶技能:TypeScript 类型体操、RxJS 流式编程以及错误处理。面试官会通过这几类题目考察你的类型推导能力、响应式编程思维和异常场景设计水平。以下 SVG 梳理了回答问题的通用框架。

TypeScript/RxJS 进阶回答框架 类型体操 泛型约束 工具类型 条件类型推导 流式编程 数据流构建 操作符管道 switchMap 取消 错误处理 错误分类 catchError retry + 降级

面试问答一:手写高级工具类型(类型体操)

面试问题
请实现一个 DeepReadonly<T> 类型,使嵌套对象的所有属性(包括数组内部)变为只读。
面试官考察点
  • 递归条件类型与映射类型的掌握程度
  • 对 keyof、in、extends 的熟练运用
  • 能否处理数组/元组的可读性约束
回答框架
  1. 明确目标:递归将每个属性标记为 readonly,若是对象或数组则继续递归。
  2. 使用 type DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends Record<any, any> ? DeepReadonly<T[K]> : T[K]; }。
  3. 处理数组:T extends (infer U)[] ? ReadonlyArray<DeepReadonly<U>> : ...。
  4. 验证:用 type Test = DeepReadonly<{a:{b:number[]}}> 确认嵌套只读。
简短示例回答
“我首先构建一个映射类型,利用 readonly 修饰符遍历每个属性;然后通过条件类型判断值是否为对象或数组,是则递归调用 DeepReadonly。数组类型单独用 ReadonlyArray 包装元素类型,保证深层只读。”
可能追问
  • 如何处理函数类型?是否要递归函数属性?
  • 如何排除 Date、Map 等内置对象?
  • 能否用 ts-expect-error 验证副作用?
避坑提醒
避免忘记处理数组/元组;注意 Record 会误判 Function 为对象,建议使用 { [key: string]: any } 或联合排除。

面试问答二:RxJS 搜索防抖与取消(流式编程)

面试问题
在 Angular 中如何实现一个搜索输入框的自动补全功能,要求 300ms 防抖,并在切换用户时取消上一个请求?
面试官考察点
  • 组合操作符 debounceTime、distinctUntilChanged、switchMap 的使用
  • 理解 Observable 的取消与内存泄漏防范
  • 是否考虑空值过滤与加载状态管理
回答框架
  1. 从 FormControl.valueChanges 创建流。
  2. 管道:debounceTime(300) → distinctUntilChanged() → filter(value => value.length >= 2) → switchMap(term => this.http.get(...))。
  3. 使用 switchMap 自动取消前一个未完成的 HTTP 请求。
  4. 通过 untilDestroyed 操作符或 takeUntil 管理组件销毁清理。
简短示例回答
“我使用 valueChanges.pipe(debounceTime(300), distinctUntilChanged(), switchMap(searchTerm => searchService.search(searchTerm)))。switchMap 保证新的搜索自动取消上次请求,并用 takeUntil(this.destroy$) 防止内存泄漏。”
可能追问
  • 如果搜索需要缓存最近 N 个结果如何实现?
  • exhaustMap 与 switchMap 的区别?
  • 如何处理并发请求顺序错乱?
避坑提醒
不要忘记 distinctUntilChanged,否则相同搜索词也会反复请求;使用 switchMap 时注意 HTTP 错误会导致流断开,应在内部使用 catchError 并返回空 Observable。

面试问答三:Angular 中 RxJS 错误处理策略

面试问题
在 Angular 服务中,调用多个依赖 API 时如何优雅处理错误,并实现自动重试和降级展示?
面试官考察点
  • 掌握 catchError、retry、retryWhen 等错误操作符
  • 是否了解错误分类(网络错误/业务错误)和全局错误处理
  • 降级策略的设计(缓存、默认值、备用 API)
回答框架
  1. 使用 pipe 链内 catchError 捕获错误,返回 of(defaultValue) 降级。
  2. 对关键请求使用 retry(3) 自动重试网络失败。
  3. 使用 retryWhen 实现指数退避重试(delay + take)。
  4. 在服务层统一使用 HttpInterceptor 集中处理 HTTP 错误并转换为业务错误对象。
简短示例回答
“我先在 HTTP 拦截器中转换错误码为统一格式;在服务方法中使用 catchError(err => { if(err.status===0) return of( cachedData ?? []); else throw err; });对网络错误用 retryWhen(errors => errors.pipe(delay(1000), take(3))),避免频繁抖动。”
可能追问
  • 如何处理多个请求部分失败的部分成功场景?
  • catchError 放在管道的什么位置?
  • 如何实现全局加载状态的统一管理?
避坑提醒
不要将 catchError 放在最终订阅中,应尽量放在具体操作符后,避免影响后续流操作;重试时注意幂等性,POST 请求重试可能导致重复提交。

常见类型工具与操作符速查

类别 名称 用途/面试考点
TypeScript 类型体操 Partial/Required 映射类型;理解 ? 与 -? 修饰符
Pick/Omit 从类型中挑选/排除属性,常考 keyof + Exclude
ReturnType/Parameters 条件类型 + infer 推导函数签名
RxJS 流式编程 debounceTime 防抖,常用于搜索输入
switchMap/mergeMap 展平高阶 Observable,switchMap 取消上一个流
catchError/retryWhen 错误捕获与重试策略,掌握指数退避