前端面试准备 02:TypeScript 工程能力——类型系统、泛型、条件类型与“类型体操”的边界
TS 面试别只讲 keyof/extends:要能说清类型系统能力边界、如何在团队里落地、如何避免类型体操失控。附高频类型题与工程规范。
- 前端面试
- TypeScript
- 工程化
TypeScript 面试的分水岭在“工程化”:你是否能把类型系统用来降低长期维护成本,而不是写一堆炫技类型。
01 你要掌握的 TS 不是语法,而是三层能力#
- 类型建模:如何用类型表达业务约束(而不是 any 到处飞)
- 推断与约束:泛型、条件类型、infer 带来的可组合性
- 工程落地:tsconfig、严格模式、边界处理、第三方类型治理
02 类型系统核心(面试必讲)#
A. Structural typing(结构类型)#
TS 关注“形状”而不是“名义”,这会影响:
- API 兼容性
- DTO / 表单值 / 后端返回值的建模方式
B. Narrowing(类型收窄)#
你要能说清:
typeof/in/instanceof/ 自定义 type guard- 可辨识联合(discriminated union)如何让分支天然安全
03 泛型与条件类型:从“会用”到“能设计”#
- 泛型用于“把变化的部分参数化”
- 条件类型用于“按输入类型分支输出类型”
- infer 用于“从复杂结构中抽取类型变量”
高频题方向:
- 实现
DeepPartial<T>/DeepReadonly<T>的思路 ReturnType<T>/Parameters<T>的用法与边界
04 类型体操的边界(非常重要)#
面试里给出立场更加分:
- 不要为了炫技牺牲可读性
- 复杂类型要写测试(tsd)或示例用法作为“契约”
- 类型复杂到影响编译速度、IDE 卡顿,就是负收益
05 工程落地:我会怎么在团队里推 TS#
建议你能讲出一套渐进策略:
- 新模块严格模式先行(
strict: true) - 旧模块先把 any 压到边界(API 层/适配层)
- 对外 API 用可辨识联合表达状态(success/error)
- tsconfig 统一,避免项目间“各自为政”
06 自测清单#
- 我能用联合类型 + 可辨识字段表达一个复杂状态机吗?
- 我能写出一个可复用的泛型函数并让类型推断自然工作吗?
- 我能解释为什么某些类型体操不值得做吗?
07 技术细讲:把 TS 用成“工程能力”#
这一节回答面试里最常见的追问:你在真实项目里怎么用 TS 降低维护成本?
A. 类型建模:先把“不可能状态”编码掉#
最有价值的做法是用联合类型表达状态,让非法状态在编译期报错:
type Loading = { status: 'loading' };
type Success<T> = { status: 'success'; data: T };
type Failure = { status: 'error'; message: string; code?: string };
type Result<T> = Loading | Success<T> | Failure;这样 UI 层的分支会天然被“状态”约束:
function renderUser(r: Result<{ name: string }>) {
if (r.status === 'loading') return 'loading';
if (r.status === 'error') return r.message;
// r 在这里被收窄为 Success
return r.data.name;
}原理:可辨识联合(discriminated union)让 TS 的控制流分析能够在分支里自动收窄类型,从而避免大量非空判断与 any。
B. 边界治理:把 any 压到“接口层/适配层”#
TS 的正确姿势是:数据进来先校验,校验后再给强类型。
- API 层:拿到
unknown - 校验层:runtime 校验(zod / valibot / 自写 guard)
- 业务层:使用强类型
这样你能在面试里讲清“类型系统不是安全边界,运行时校验才是”。
C. 类型体操边界:可读性、编译性能、团队维护#
你可以给面试官一个明确原则(加分):
- 复杂类型必须“能读懂 + 有示例 + 有约束”
- 如果 IDE 明显变慢/编译明显变慢,说明类型复杂度失控
- 与其做深度类型体操,不如在边界做 runtime 校验
08 练习题(笔试 + 代码 + 参考答案推导)#
题 1(笔试):unknown 与 any 的差异是什么?为什么工程里更推荐 unknown?#
参考答案(原理)#
any:关闭类型检查,污染会扩散;它既能赋值给任何类型,也能被任何类型赋值。unknown:安全的顶层类型,必须先收窄(type guard)才能使用;不会把不安全传播到下游。
工程推荐 unknown 的原因是:让“不确定性”停留在边界,逼迫你做校验/收窄。
题 2(编码):实现一个类型守卫 isNonEmptyString#
export function isNonEmptyString(x: unknown): x is string {
// TODO
}参考答案(原理 + 代码)#
原理:type predicate x is string 会告诉 TS:在返回 true 的分支里,x 可被视为 string。
export function isNonEmptyString(x: unknown): x is string {
return typeof x === 'string' && x.trim().length > 0;
}题 3(编码 + 类型设计):实现一个强类型的 pick#
export function pick<T extends object, K extends keyof T>(
obj: T,
keys: readonly K[],
): Pick<T, K> {
// TODO
}参考答案(原理 + 代码)#
原理:
K extends keyof T约束 keys 只能选 obj 的键- 返回类型
Pick<T, K>保证只包含选中的键
export function pick<T extends object, K extends keyof T>(
obj: T,
keys: readonly K[],
): Pick<T, K> {
const out = {} as Pick<T, K>;
for (const k of keys) out[k] = obj[k];
return out;
}题 4(思考题):什么时候不该用 TS 类型体操?#
参考答案(要点)#
- 当类型复杂到“团队没人敢改”
- 当类型推断导致 IDE 卡顿、编译变慢
- 当问题本质是运行时不可信数据(应先 runtime 校验)
下一篇进入浏览器与网络:渲染流水线、缓存与 HTTP 追问会非常密集。
相关阅读
基于内容相似度 + 标签/系列加权
- 1 分钟前端面试准备(8 年一线工程师知识体系与学习路线)
前端面试准备 05:工程化——构建、依赖治理、质量体系、CI/CD 与发布回滚
工程化题的本质是“可持续交付”。从构建与依赖图、代码分割、质量门禁、测试金字塔到灰度发布与回滚,把面试回答讲成“我在团队里怎么做”。
- 前端面试
- 工程化
- CI/CD
- 2 分钟前端面试准备(8 年一线工程师知识体系与学习路线)
前端面试准备 04:React 核心——渲染、更新、Hooks 心智模型、并发与 SSR(面试表达模板)
用“渲染=计算 UI”与“更新=调度”两句话讲清 React:Hooks 规则、闭包陷阱、性能优化点、并发渲染与 SSR/Streaming 的取舍。
- 前端面试
- React
- Hooks
- 2 分钟前端面试准备(8 年一线工程师知识体系与学习路线)
前端面试准备 03:浏览器与网络——渲染流水线、缓存、CORS、Cookie、HTTP/2(高频追问一网打尽)
把浏览器与网络题整理成“从输入到像素”的链路:渲染流水线、回流重绘、合成、缓存策略、CORS、Cookie/SameSite、HTTP/2/3。
- 前端面试
- 浏览器
- 网络
- 7 分钟前端面试准备(8 年一线工程师知识体系与学习路线)
前端面试准备 01:JavaScript 运行时——执行上下文、闭包、原型链与事件循环(能推导版)
把 JS 面试高频点组织成“可推导”的模型:执行上下文/作用域链/this/原型链/事件循环。附典型追问、手写题拆解与易错边界。
- 前端面试
- JavaScript
- 运行时