TypeScript与RxJS进阶面试:类型体操、流式编程、错误处理、考察点
本章聚焦 Angular 跨端开发中最核心的进阶技能:TypeScript 类型体操、RxJS 流式编程以及错误处理。面试官会通过这几类题目考察你的类型推导能力、响应式编程思维和异常场景设计水平。以下 SVG 梳理了回答问题的通用框架。
面试问答一:手写高级工具类型(类型体操)
- 面试问题
- 请实现一个
DeepReadonly<T>类型,使嵌套对象的所有属性(包括数组内部)变为只读。 - 面试官考察点
-
- 递归条件类型与映射类型的掌握程度
- 对
keyof、in、extends的熟练运用 - 能否处理数组/元组的可读性约束
- 回答框架
-
- 明确目标:递归将每个属性标记为
readonly,若是对象或数组则继续递归。 - 使用
type DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends Record<any, any> ? DeepReadonly<T[K]> : T[K]; }。 - 处理数组:
T extends (infer U)[] ? ReadonlyArray<DeepReadonly<U>> : ...。 - 验证:用
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 的取消与内存泄漏防范
- 是否考虑空值过滤与加载状态管理
- 组合操作符
- 回答框架
-
- 从
FormControl.valueChanges创建流。 - 管道:
debounceTime(300)→distinctUntilChanged()→filter(value => value.length >= 2)→switchMap(term => this.http.get(...))。 - 使用
switchMap自动取消前一个未完成的 HTTP 请求。 - 通过
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)
- 掌握
- 回答框架
-
- 使用
pipe链内catchError捕获错误,返回of(defaultValue)降级。 - 对关键请求使用
retry(3)自动重试网络失败。 - 使用
retryWhen实现指数退避重试(delay+take)。 - 在服务层统一使用
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 |
错误捕获与重试策略,掌握指数退避 |