TypeScript 面试考察的是你对类型系统的理解,而不只是语法。下面整理了 20 道面试题及参考答案,从基础概念到那些能区分“会用 TypeScript”和“懂它为什么这样工作”的进阶问题。文中代码在发布前均已通过 TypeScript 编译器严格模式编译,引用的报错信息都是真实的。
基础
1. TypeScript 给 JavaScript 增加了什么?
一套在编译期检查、之后被擦除的静态类型系统。输出结果是纯 JavaScript,所以类型既没有运行时开销,也不存在于运行时。最后这点很重要:除非你自己写检查,否则无法在运行时用 typeof 检查一个类型。
2. type 和 interface 有什么区别?
两者都用来描述对象结构,很多时候可以互换使用。实际区别如下:
| interface | type | |
|---|---|---|
| 声明合并 | 可以,两次声明会合并 | 不行,重名会报错 |
| 联合类型和原始类型 | 无法表达 | 可以表达,比如 number 和 string 的联合 |
| 继承 | extends | 用 & 做交叉类型 |
| 映射类型和条件类型 | 不支持 | 支持 |
声明两次 interface A 会合并字段。声明两次 type B 会报 error TS2300: Duplicate identifier 'B'。一个常用的经验法则:对外暴露、可能被别人扩展的对象结构用 interface,其他情况用 type。
3. any、unknown 和 never 有什么区别?
- any 会关闭类型检查。什么都能赋值,错误也被隐藏了。
- unknown 是安全版本。任何值都可以赋给它,但使用前必须先收窄类型。对 unknown 调用 x.toFixed() 会报 error TS18046: 'x' is of type 'unknown'。
- never 表示不可能存在的值的类型,比如一个总是抛异常的函数。它常用于穷尽性检查。
对于类型不受你控制的值,比如解析出来的 JSON,优先用 unknown 而不是 any。
4. strict 模式做了什么?
它会开启一组检查,最重要的是 strictNullChecks(让 null 和 undefined 成为独立类型)和 noImplicitAny。开启后,不检查就直接使用可选值会报 error TS18048: 'x' is possibly 'undefined'。新项目应该一开始就打开 strict。
5. readonly 和 const 有什么区别?
const 阻止变量被重新赋值。readonly 阻止属性被赋值。给 readonly 属性赋值会报 error TS2540: Cannot assign to 'n' because it is a read-only property。两者都不会让底层对象变成深度不可变。
类型实战
6. 什么是联合类型,如何收窄它?
联合类型表示“这些类型之一”。收窄用的是编译器能理解的检查:
type Id = number | string;
function describe(id: Id): string {
if (typeof id === "string") return id.toUpperCase();
return id.toFixed(1);
}
在 if 内部,id 是 string。if 之后,编译器知道它是 number。其他收窄手段还有 instanceof、in 运算符、相等判断以及自定义类型守卫。
7. 什么是可辨识联合?
联合类型中每个成员都有一个共享的字面量字段,也就是判别字段,用来区分它们:
type Shape =
| { kind: "circle"; r: number }
| { kind: "sq"; s: number };
function assertNever(x: never): never {
throw new Error("unexpected " + x);
}
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.r ** 2;
case "sq": return s.s ** 2;
default: return assertNever(s);
}
}
如果之后新增了一种结构却忘了加对应的 case,default 分支就会编译失败,因为此时 s 不再是 never。这就是穷尽性检查。
8. 什么是泛型,为什么要用它?
泛型让函数或类型能适用于多种类型,同时保留它们之间的关系:
function first<T>(xs: T[]): T | undefined { return xs[0]; }
function pick<T, K extends keyof T>(o: T, k: K): T[K] { return o[k]; }
first([1, 2]) 返回 number | undefined,而不是 any。pick(user, "name") 返回 name 的类型。extends keyof T 约束能阻止你传入不存在的键。
9. 常见的工具类型有哪些?
| 工具类型 | 作用 |
|---|---|
| Partial<T> | 让所有属性变为可选 |
| Required<T> | 让所有属性变为必填 |
| Readonly<T> | 让所有属性变为只读 |
| Pick<T, K> | 只保留列出的键 |
| Omit<T, K> | 移除列出的键 |
| Record<K, V> | 键为 K、值为 V 的对象类型 |
| ReturnType<F> | 函数类型的返回类型 |
10. 什么是映射类型?
它通过遍历另一个类型的键来构造新类型。Partial 本身就是写成 { [K in keyof T]?: T[K] }。理解这一点说明你是真的懂工具类型,而不是死记硬背。
11. 什么是 as const?
它告诉编译器推断出最窄的类型,并把所有内容标记为只读。const arr = [1, 2, 3] as const 的类型是 readonly [1, 2, 3],而 arr.push(4) 会报 error TS2339: Property 'push' does not exist on type 'readonly [1, 2, 3]'。它适合用于允许值的列表和配置对象。
12. 什么是类型断言,什么时候有风险?
value as Type 是告诉编译器相信你。它在运行时不做任何检查。如果你判断错了,错误会在之后以崩溃的形式暴露出来。优先用检查或类型守卫函数来收窄类型,而不是直接断言。
语言行为
13. null 和 undefined 有什么区别?
undefined 表示从未赋过值,而 null 表示有意设置的“无值”。在启用 strictNullChecks 时,它们是两种不同的类型。可以用可选链(a?.b)和空值合并运算符(a ?? b)来处理它们。与 || 不同,?? 只在 null 和 undefined 时才回退,因此 0 和空字符串会被保留。
14. 非空断言 ! 是什么?
value! 告诉编译器某个值不是 null 或 undefined。和 as 一样,它不做检查。在你比编译器更清楚情况的少数场景下可以用,但通常做一次运行时检查更安全。
15. keyof 有什么作用?类型位置上的 typeof 又是什么?
keyof T 生成 T 所有属性名组成的联合类型。在类型位置上,typeof x 取某个值的类型,从而避免重复书写。两者结合,keyof typeof obj 就能得到你以值的形式定义的对象的键。
16. 枚举和联合类型相比如何?
枚举会创建一个运行时对象和对应的值。而字符串字面量联合类型,比如 "idle" | "loading",在编译时就被擦除,不需要额外的代码。很多团队为了简洁更倾向于字符串字面量联合类型,只有在需要运行时对象时才使用枚举。
工具链与真实项目
17. tsconfig.json 有什么作用?哪些选项比较重要?
它告诉编译器要包含哪些文件以及如何检查它们。面试官常问的选项有:strict、target(要输出的 JavaScript 版本)、module 和 moduleResolution、noEmit(只做检查,在由其他工具负责构建时很常见)、esModuleInterop 以及 skipLibCheck。
18. 如何为来自外部的数据(比如 API 响应)标注类型?
类型在运行时会被擦除,所以编译器无法验证网络返回的内容。应把数据当作 unknown,然后用 schema 库或手写的类型守卫来校验,再使用它。直接对解析后的 JSON 断言类型会掩盖 bug。
19. 如何把 JavaScript 项目迁移到 TypeScript?
要循序渐进。先添加带 allowJs 的 tsconfig.json,一次重命名一个文件,逐步开启更严格的选项,并优先在边界处添加类型,比如 API 客户端和共享工具函数。避免一次性大重写。
20. 什么是结构化类型?
TypeScript 按形状而非名称来比较类型。只要一个对象具备某个类型所要求的字段,就会被接受,即使它从未被声明为该类型。这与采用名义类型的语言不同,后者中声明的名称才重要。
如何准备
- 打开一个 playground 或在严格模式下建一个小项目,把上面每个例子都试一遍,包括那些应该报错的例子。
- 准备好凭记忆写一个泛型函数和一个可辨识联合类型。
- 明白为什么你会选择 unknown 而不是 any。
- 准备一个在真实项目中为棘手内容标注类型的故事。
相关练习可参考我们的指南:Node.js 面试题、Angular 面试题 和 Python 面试题。
大声练习,并用自己的话表达。事后的文字记录能看出你在哪里啰嗦或跑题了。在任何现场面试或评估中,都要遵守雇主关于 AI 工具的规定。
用 YesToTheOffer 练习你的回答
大声排练,根据你自己的简历生成答案,并在之后回顾你的转录。30 分钟免费,无需订阅。



