类型收窄(Type Narrowing)是 TypeScript 将宽泛类型缩小为更具体类型的过程。比如一个参数是 string | number,在某个分支中 TS 推断出它就是 string。掌握这 5 种收窄方式,可以让你写出更安全、更简洁的类型代码。
// 类型收窄的 5 种方式
// 方式一:typeof 类型收窄
function padLeft(value: string | number, padding: string | number) {
if (typeof padding === 'number') {
// padding 收窄为 number
return Array(padding + 1).join(' ') + value;
}
if (typeof padding === 'string') {
// padding 收窄为 string
return padding + value;
}
throw new Error('Invalid padding');
}
// 方式二:instanceof 类型收窄
class ApiError extends Error {
constructor(public code: number, message: string) {
super(message);
}
}
function handleError(err: unknown) {
if (err instanceof ApiError) {
// err 收窄为 ApiError
console.log(`错误码: ${err.code}, 消息: ${err.message}`);
} else if (err instanceof Error) {
// err 收窄为 Error
console.log(err.message);
}
}
// 方式三:in 操作符收窄
interface Fish { swim: () => void; }
interface Bird { fly: () => void; }
function move(animal: Fish | Bird) {
if ('swim' in animal) {
// animal 收窄为 Fish
animal.swim();
} else {
// animal 收窄为 Bird
animal.fly();
}
}
// 方式四:真值判断收窄(排除 null/undefined)
function printAll(strs: string | string[] | null) {
if (strs && typeof strs === 'object') {
// strs 收窄为 string[](排除了 null 和 string)
strs.forEach(s => console.log(s));
} else if (typeof strs === 'string') {
// strs 收窄为 string
console.log(strs);
}
}
// 方式五:断言函数收窄(asserts 关键字)
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Not a string!');
}
}
function processValue(value: unknown) {
assertIsString(value);
// 断言函数执行后,value 被收窄为 string
console.log(value.toUpperCase());
}
typeof null === 'object'。if (value) 可以排除 null、undefined、0、''、NaN、false 等假值。注意 0 和空字符串也会被过滤。asserts value is Type 是 TS 3.7 引入的断言函数,不返回布尔值,而是通过抛异常来收窄类型。调用后代码的类型自动收窄。// 实战:处理 API 请求的多种状态
type RequestState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
function render<T>(state: RequestState<T>) {
switch (state.status) {
case 'idle':
return '请发起请求';
case 'loading':
return '加载中...';
case 'success':
// state 被收窄为 { status: 'success'; data: T }
return JSON.stringify(state.data);
case 'error':
// state 被收窄为 { status: 'error'; error: Error }
return state.error.message;
default:
// never 检查:确保穷尽了所有可能
const _exhaustive: never = state;
return _exhaustive;
}
}