跳到主要内容

anyunknown 有什么区别?

下文默认基于 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. 最关键的区别:后续能不能直接用

对比项anyunknown
是否能接收任意值
是否能直接读属性/调方法不能
是否能直接赋给具体类型基本都能不能,需收窄或断言
类型安全最弱强很多
典型用途临时逃生、旧代码迁移外部输入、边界数据、错误对象

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 unknownany 都能表示“不确定”,为什么还需要两个

因为它们表达的是两种完全不同的工程态度:

  • 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,再做类型收窄。