any 和 unknown 有什么区别?
下文默认基于 TypeScript 5.x,并假设开启
strict: true。
面试速答(30 秒版 TL;DR)
any表示“我放弃这部分类型检查”,它会污染上下游类型安全。unknown表示“我现在不知道它是什么类型”,但必须先缩小范围才能安全使用。- 两者都能接收任意值,但后续约束完全不同:
any:能直接读属性、调方法、参与运算;unknown:不行,必须先做类型收窄(narrowing)或断言。
- 面试里一句话总结:
any是逃生舱;unknown是安全兜底。
心智模型:一个是放弃检查,一个是延迟判断
1. any 的本质:退出类型系统
let value: any = "hello";
value.foo.bar();
value();
value = 123;
编译器基本不会拦你。
这意味着:
- 你获得了“写起来很自由”的感觉;
- 也失去了 TypeScript 最核心的静态保护。
更麻烦的是,any 会扩散:
function getData(): any {
return { name: "Tom" };
}
const data = getData();
data.notExists.call();
只要入口是 any,后面很多地方都可能一起失守。
2. unknown 的本质:安全的顶层类型
let value: unknown = "hello";
value = 123;
value = { name: "Tom" };
它也能接任意值,但下面这样不行:
value.toUpperCase();
value.name;
value();
因为编译器会提醒你:
- 你只是“收到了一个未知值”;
- 但还没有证明它真的是字符串、对象或函数。
3. 最关键的区别:后续能不能直接用
| 对比项 | any | unknown |
|---|---|---|
| 是否能接收任意值 | 能 | 能 |
| 是否能直接读属性/调方法 | 能 | 不能 |
| 是否能直接赋给具体类型 | 基本都能 | 不能,需收窄或断言 |
| 类型安全 | 最弱 | 强很多 |
| 典型用途 | 临时逃生、旧代码迁移 | 外部输入、边界数据、错误对象 |
4. 为什么 unknown 更适合边界层
典型边界层包括:
JSON.parse的结果;- 后端接口返回;
catch (error);postMessage、本地存储、URL 参数。
这些数据进入系统时,最合理的态度不是“我相信它对”,而是:
- 先用
unknown接住; - 再通过校验收窄成业务需要的类型。
function parseUser(json: string): unknown {
return JSON.parse(json);
}
后面再判断:
function isUser(value: unknown): value is { id: number; name: string } {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value
);
}
5. 赋值关系怎么理解
5.1 任意值都能赋给 unknown
let v: unknown;
v = 1;
v = "hello";
v = { ok: true };
5.2 unknown 不能直接赋给更具体的类型
let v: unknown = "hello";
const name: string = v; // 报错
因为你还没证明 v 真的是 string。
5.3 any 几乎到处都能赋
let v: any = "hello";
const name: string = v;
const count: number = v;
这正是它危险的来源。
6. 面试高频追问
6.1 unknown 和 any 都能表示“不确定”,为什么还需要两个
因为它们表达的是两种完全不同的工程态度:
any:我不检查了;unknown:我先不下结论,但后面必须验证。
6.2 什么时候可以用 any
可以用,但要克制。常见合理场景:
- 老项目迁移 TypeScript 的过渡阶段;
- 三方库类型严重缺失,短期又必须接入;
- 非常局部的底层封装,需要先打通链路再逐步补类型。
原则是:
- 尽量把
any封在边界里; - 不要让它流到核心业务代码里。
6.3 catch (error) 为什么很多团队建议用 unknown
因为运行时抛出来的不一定是 Error 实例,理论上可以是任意值:
throw "network failed";
throw 500;
throw { message: "bad request" };
所以先当成 unknown,再判断更稳。
7. 常见误区
- 误区 1:把
any当成“高级版动态语言”。- 结果往往是 TypeScript 在最关键的地方失效。
- 误区 2:以为
unknown没法用。- 实际上它是鼓励你在正确的位置做校验。
- 误区 3:边界层一上来就断言
as Xxx。- 这只是把风险推迟,不是真正验证。
速记要点
any= 放弃检查,unknown= 未知但安全。- 两者都能接任意值,但
unknown不能直接使用。 - 面向外部输入时,优先
unknown,再做类型收窄。