在 TypeScript 中,never 是一个特殊类型,表示「永不存在的值类型」,通常用于表示不可能发生的情况。它适用于抛出异常、不返回值的函数或处理逻辑上永远不会出现的分支。
以下是它的简单用法和注意事项:
1. never 的用法
1、抛出异常的函数:当函数会抛出错误并终止执行时,返回类型应定义为 never。
🌰:
function throwError(message: string): never {throw new Error(message);
}
2、死循环的函数:永远不会结束的函数(例如:无限循环)也可以使用 never 类型。
🌰:
function infiniteLoop(): never {while (true) {}
}
3、类型保护中的检查:在类型缩小过程中,用 never 检查不可能的情况,有助于提高代码的严格性和可读性。
🌰:
function handleValue(value: string | number) {if (typeof value === 'string') {console.log('String value');} else if (typeof value === 'number') {console.log('Number value');} else {// 如果 value 类型是 never,编译器会检查这里的情况是否可能发生const _exhaustiveCheck: never = value;throw new Error('Unexpected type: ' + value);}
}
handleValue('string');
handleValue(1);
handleValue(true);
2. never 的神奇
1、确保类型全面覆盖:在处理 switch 或多类型分支时,never 可用于强制检查是否所有可能的分支都被覆盖,帮助捕获遗漏的情况。
🌰:假设有个表示用户状态的联合类型 UserStatus:
type UserStatus = "active" | "inactive" | "banned";function handleUserStatus(status: UserStatus) {switch (status) {case "active":console.log("User is active");break;case "inactive":console.log("User is inactive");break;case "banned":console.log("User is banned");break;default:// 强制类型检查是否已全面覆盖所有情况const exhaustiveCheck: never = status;throw new Error(`Unhandled status: ${status}`);}
}
在 switch 语句的 default 分支中,我们定义了一个 exhaustiveCheck 变量,类型为 never,并将 status 赋值给它。TypeScript 会在编译时检查,如果 status 不是 never 类型(即还存在未处理的情况),会触发编译错误。
比如:未来在 UserStatus 中添加新的状态,例如 suspended,但没有在 switch 语句中处理它,TypeScript 会报错:
type UserStatus = 'active' | 'inactive' | 'banned' | 'suspended';
这种方式确保了新增状态时的类型全面覆盖,有助于维护代码的健壮性和可读性。
2、类型收窄的辅助:never 可以帮助 TS 判断哪些代码路径无法访问,从而优化代码检查和类型收窄。
🌰:
// 在类型约束中,表示参数的类型不是某一个类型,比如:类型除了 number 之外都OK
type MyExclude<T, U> = T extends U ? never : T;
// function isNumber<T>(x: T extends number ? never : T) {}
function isNumber<T>(x: MyExclude<T, number>) {}
isNumber('123')
isNumber({})
isNumber(true)
isNumber(111)
3. 判断 never 类型
在 TypeScript 中,当使用条件类型 T extends never ? true : false 直接判断 never 类型时,得到 never 而不是 true。
1、为什么会这样呢?
在条件类型中,当 T 是联合类型时,TypeScript 会对 T 中的每个成员独立应用条件(即分配式条件类型)。但是 never 是一个“空的联合类型”,没有成员,因此 TypeScript 无法真正地应用条件,它直接返回 never。
2、分配式条件类型
条件类型在遇到联合类型时,会逐一分配并进行条件判断,即所谓的分配式。不过,never 并不属于任何集合,因此在分配式条件下,会直接返回 never,而不会像其他类型一样返回 true 或 false。
3、解决方法:
为了让条件类型正确地判断 never,可以将 T 包装在元组中,以防止分配式条件类型的影响:
type IsNever<T> = [T] extends [never] ? true : false;
这样会强制 T 作为一个整体进行判断,因此 never 就会得到 true。
🌰:
type Test1 = IsNever<never>; // true
type Test2 = IsNever<string>; // false
type Test3 = IsNever<undefined>; // false
type Test4 = IsNever<any>; // false
4. 注意事项
1、避免滥用:never 仅适用于不可能有结果的情况;如果函数可能有返回值,使用 void 更合适。
2、和 void 的区别:void 表示函数无返回值,但仍会返回 undefined,而 never 表示函数根本不会返回。
3、使用在类型收窄中:never 可以帮助 TypeScript 识别和优化代码分支,若分支逻辑不全,编译器会警告。