TypeScript面试避坑指南:这些高频问题你真的掌握了吗?
最近几年,TypeScript在前端技术栈中的地位越来越稳固,几乎成了中大型项目的标配。我面试过不少候选人,发现一个有趣的现象:很多人简历上写着“熟练掌握TypeScript”,但一深入追问细节,特别是那些看似基础却容易混淆的概念,回答往往不尽如人意。面试官真正想看的,不是你背了多少API,而是对类型系统设计理念的理解深度,以及在实际项目中如何避免常见的类型陷阱。
这篇文章我想换个角度,不从“应该掌握什么”出发,而是聚焦于“面试中容易栽跟头的地方”。我会结合自己作为面试官的经验,拆解那些候选人最常犯错的类型场景,通过对比正确和错误的代码示例,帮你理清思路。无论你是准备跳槽的中高级开发者,还是想系统提升TypeScript水平,这篇文章都会带你避开那些看似简单实则暗藏玄机的坑。
1. 类型系统的核心陷阱:any、unknown与never的误用
很多开发者对any的态度很矛盾——既知道要避免,又在某些场景下忍不住使用。但更危险的是,很多人分不清unknown和any的实际区别,或者对never类型的理解停留在表面。
1.1 any的诱惑与代价
先看一个典型的错误示例:
// 错误示范:滥用any导致类型安全完全失效
function processUserData(data: any) {
console.log(data.name.toUpperCase()); // 运行时可能报错!
return data.id + 100; // 可能不是数字
}
// 调用时传入错误类型
processUserData({ name: null, id: "100" }); // 编译通过,运行时崩溃
这里的问题在于,一旦使用了any,TypeScript就放弃了所有类型检查。更隐蔽的问题是,any会传染——一个any值赋值给其他变量,那些变量也会变成any。
正确的做法应该是尽可能使用更精确的类型。如果确实无法确定类型,应该使用unknown:
// 正确做法:使用unknown并配合类型守卫
function processUserData(data: unknown) {
if (typeof data === 'object' && data !== null && 'name' in data) {
// 现在TypeScript知道data有name属性
const userData = data as { name: string; id?: number };
console.log(userData.name.toUpperCase());
if (typeof userData.id === 'number') {
return userData.id + 100;
}
}
throw new Error('Invalid user data');
}
1.2 unknown与any的本质区别
很多人知道unknown比any更安全,但说不清楚具体区别。关键在于:unknown是"顶级类型"中最严格的,而any既是顶级类型又是底层类型。
| 特性 | any |
unknown |
|---|---|---|
| 赋值给其他类型 | ✅ 允许 | ❌ 不允许(除非类型断言) |
| 访问属性/方法 | ✅ 允许 | ❌ 不允许 |
| 作为函数参数 | ✅ 允许 | ✅ 允许 |
| 类型收缩要求 | ❌ 不需要 | ✅ 必须通过类型守卫 |
看这个对比:
let anyValue: any = "hello";
let unknownValue: unknown = "world";
// any可以随意操作
anyValue.toUpperCase(); // 编译通过
anyValue.nonExistentMethod(); // 编译通过(危险!)
// unknown必须先进行类型检查
// unknownValue.toUpperCase(); // 编译错误:Object is of type 'unknown'
if (typeof unknownValue === 'string') {
unknownValue.toUpperCase(); // 现在可以了
}
1.3 never类型的真正含义
never类型可能是TypeScript中最容易被误解的类型之一。它不是"没有值",而是"永远不会有值到达这里"。
常见误区1:认为never和void差不多
// 错误理解
function error(): void {
throw new Error('Oops');
}
// 正确:应该返回never
function error(): never {
throw new Error('Oops'); // 函数永远不会正常返回
}
常见误区2:不理解never在条件类型中的应用
// 一个实用的例子:过滤掉null和undefined
type NonNullable<T> = T extends null | undefined ? never : T;
// 测试
type T1 = NonNullable<string | null>; // string
type T2 = NonNullable<string | undefined | number>; // string | number
// 原理:当T是null时,返回never,而never在联合类型中会被忽略
// string | never 等价于 string
在实际项目中,never常用于表示不可能发生的状态,比如 exhaustive check:
type Shape = 'circle' | 'square' | 'triangle';
function getArea(shape: Shape, size: number): number {
switch (shape) {
case 'circle':
return Math.PI * size * size;
case 'square':
return size * size;
case 'triangle':
return (Math.sqrt(3) / 4) * size * size;
default:
// 如果Shape类型新增了值,这里会报错
const _exhaustiveCheck: never = shape;
throw new Error(`Unhandled shape: ${_exhaustiveCheck}`);
}
}
2. 类型断言的双刃剑:as与尖括号的陷阱
类型断言是TypeScript中非常有用的特性,但也是面试中最容易暴露问题的部分。很多候选人知道怎么用,但不知道什么时候该用,更不知道滥用类型断言的后果。
2.1 类型断言 vs 类型声明
先看一个常见的混淆场景:
interface User {
name: string;
age: number;
}
// 方式1:类型断言(告诉编译器"相信我")
const user1 = {} as User;
user1.name = "John";


2217

被折叠的 条评论
为什么被折叠?



