any、unknown、never 是 TypeScript 中三个特殊且容易混淆的类型。它们分别代表"任意类型"、"未知类型"和"永不发生的类型"。理解它们的区别和适用场景,是写出安全 TypeScript 代码的基础。
// any vs unknown vs never 的完整对比
// ==================== any ====================
// 放弃类型检查,可以赋值给任何变量,也可以访问任何属性
let a: any = 'hello';
a = 42; // OK
a = true; // OK
a.foo.bar.baz(); // OK(编译通过,运行时崩溃)
const s: string = a; // OK,any 可以赋值给任何类型
const n: number = a; // OK
// ==================== unknown ====================
// 类型安全的 any,不能直接使用,必须先收窄
let u: unknown = 'hello';
u = 42; // OK
u = true; // OK
// u.foo.bar.baz(); // 错误!unknown 不能访问属性
// const s2: string = u; // 错误!unknown 不能赋值给 string
// 正确用法:先收窄再使用
if (typeof u === 'string') {
const s3: string = u; // OK,u 已收窄为 string
console.log(u.toUpperCase());
}
// ==================== never ====================
// 表示永远不会发生的类型,用于穷尽性检查
// never 是任何类型的子类型,可以赋值给任何类型
const nev: never = (() => { throw new Error('never'); })() as never;
const str: string = nev; // OK,never 可以赋值给任何类型
const num: number = nev; // OK
// 没有任何类型可以赋值给 never(除了 never 本身)
// const n2: never = 'hello'; // 错误!
// 实战:穷尽性检查
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
| { kind: 'triangle'; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.side ** 2;
case 'triangle':
return (shape.base * shape.height) / 2;
default:
// 如果 Shape 新增了类型但忘记添加 case,这里会编译报错
const _exhaustive: never = shape;
return _exhaustive;
}
}
// 实战:API 返回 unknown 后类型守卫收窄
interface User {
id: number;
name: string;
}
// 模拟 fetch:返回 unknown
async function fetchData(url: string): Promise<unknown> {
const res = await fetch(url);
return res.json();
}
function isUser(data: unknown): data is User {
return (
typeof data === 'object' &&
data !== null &&
'id' in data &&
'name' in data
);
}
// 使用
async function getUser() {
const data = await fetchData('/api/user/1');
if (isUser(data)) {
// data 被收窄为 User
console.log(data.name);
} else {
console.error('Invalid user data');
}
}