🎁 立即注册,最多可免费使用 30 分钟在线 AI,无需信用卡。

TypeScript 面试问题与答案(2026):类型、泛型和类型收窄

October 9, 2026
20 个 TypeScript 面试问题及解答,在严格模式下测试:type 与 interface、any 与 unknown、联合类型、泛型、工具类型和结构化类型。
TypeScript 面试问题与答案(2026):类型、泛型和类型收窄
typescript 面试问题
typescript 面试
type 与 interface
any 与 unknown typescript
typescript 泛型面试

TypeScript 面试考察的是你对类型系统的理解,而不只是语法。下面整理了 20 道面试题及参考答案,从基础概念到那些能区分“会用 TypeScript”和“懂它为什么这样工作”的进阶问题。文中代码在发布前均已通过 TypeScript 编译器严格模式编译,引用的报错信息都是真实的。

基础

1. TypeScript 给 JavaScript 增加了什么?

一套在编译期检查、之后被擦除的静态类型系统。输出结果是纯 JavaScript,所以类型既没有运行时开销,也不存在于运行时。最后这点很重要:除非你自己写检查,否则无法在运行时用 typeof 检查一个类型。

2. type 和 interface 有什么区别?

两者都用来描述对象结构,很多时候可以互换使用。实际区别如下:

interfacetype
声明合并可以,两次声明会合并不行,重名会报错
联合类型和原始类型无法表达可以表达,比如 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 按形状而非名称来比较类型。只要一个对象具备某个类型所要求的字段,就会被接受,即使它从未被声明为该类型。这与采用名义类型的语言不同,后者中声明的名称才重要。

如何准备

  1. 打开一个 playground 或在严格模式下建一个小项目,把上面每个例子都试一遍,包括那些应该报错的例子。
  2. 准备好凭记忆写一个泛型函数和一个可辨识联合类型。
  3. 明白为什么你会选择 unknown 而不是 any。
  4. 准备一个在真实项目中为棘手内容标注类型的故事。

相关练习可参考我们的指南:Node.js 面试题、Angular 面试题 和 Python 面试题。

大声练习,并用自己的话表达。事后的文字记录能看出你在哪里啰嗦或跑题了。在任何现场面试或评估中,都要遵守雇主关于 AI 工具的规定。

用 YesToTheOffer 练习你的回答

大声排练,根据你自己的简历生成答案,并在之后回顾你的转录。30 分钟免费,无需订阅。

iOS & Android — 即将推出
查看价格