never类型与void类型
在TypeScript中,never
和void
是两种特殊的类型,它们用于表示函数的返回值,但语义和用途截然不同。void
表示函数没有返回值,而never
表示函数永远不会正常返回。理解它们的区别对于编写类型安全的代码至关重要。
void
类型的基本概念
void
类型表示函数没有返回值。当函数执行完毕后,不会返回任何值(或者更准确地说,返回undefined
)。这是JavaScript中默认的函数行为,TypeScript通过void
类型显式标注这一点。例如:
function logMessage(message: string): void {
console.log(message);
}
这里,logMessage
函数没有return
语句,因此它的返回类型是void
。如果尝试显式返回一个值,TypeScript会报错:
function invalidVoidExample(): void {
return "This will cause an error"; // Error: Type 'string' is not assignable to type 'void'
}
void
与undefined
的关系
在TypeScript中,void
和undefined
看似相似,但有细微差别。void
表示“没有返回值”,而undefined
是一个具体的值。如果函数显式返回undefined
,它的类型可以是void
或undefined
:
function returnsUndefined(): undefined {
return undefined; // 合法
}
function alsoReturnsUndefined(): void {
return undefined; // 也合法,因为void允许返回undefined
}
但反过来不行:
function invalidUndefinedExample(): undefined {
// Error: A function whose declared type is neither 'void' nor 'any' must return a value
}
never
类型的基本概念
never
类型表示函数永远不会正常返回。它通常用于以下场景:
- 函数总是抛出异常。
- 函数包含无限循环。
- 类型收窄后不可能存在的分支。
抛出异常的函数
如果一个函数总是抛出错误,它永远不会有机会返回值,因此返回类型是never
:
function throwError(message: string): never {
throw new Error(message);
}
无限循环的函数
类似地,如果一个函数包含无限循环,它的返回类型也是never
:
function infiniteLoop(): never {
while (true) {
// 无限执行
}
}
类型收窄中的never
在类型守卫或条件类型中,never
可以表示不可能存在的分支。例如:
type NonString<T> = T extends string ? never : T;
function filterStrings<T>(value: T): NonString<T> {
if (typeof value === "string") {
throw new Error("Strings are not allowed");
}
return value; // 类型为NonString<T>
}
void
与never
的关键区别
-
语义差异
void
:函数正常执行完毕,但没有返回值。never
:函数无法正常执行完毕。
-
可赋值性
void
可以被赋值为undefined
或null
(在strictNullChecks
关闭时)。never
是所有类型的子类型,可以赋值给任何类型,但没有任何类型可以赋值给never
(除了never
本身)。
let v: void = undefined; // 合法
let n: never = throwError("error"); // 只有通过never函数赋值
// 以下代码会报错
let invalidNever: never = 42; // Error: Type 'number' is not assignable to type 'never'
- 在联合类型中的行为
void
在联合类型中会被保留。never
在联合类型中会被忽略,因为它表示“不可能存在”。
type UnionWithVoid = string | void; // string | void
type UnionWithNever = string | never; // string
实际应用场景
void
的典型用法
- 事件处理函数
许多事件处理函数没有返回值,适合用void
标注:
button.addEventListener("click", (): void => {
console.log("Button clicked");
});
- React组件中的副作用
在React中,如果组件方法仅用于副作用操作,可以声明为void
:
class MyComponent extends React.Component {
handleClick(): void {
this.setState({ clicked: true });
}
}
never
的典型用法
- 全面性检查
在switch
或if-else
链中,never
可以确保所有可能的情况都被处理:
type Shape = "circle" | "square" | "triangle";
function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI * 2;
case "square":
return 4;
case "triangle":
return 3;
default:
const exhaustiveCheck: never = shape; // 如果Shape有新增类型,这里会报错
throw new Error(`Unknown shape: ${exhaustiveCheck}`);
}
}
- 高阶类型工具
在条件类型中,never
常用于过滤或组合类型:
type ExtractStrings<T> = T extends string ? T : never;
type Result = ExtractStrings<"a" | 1 | true>; // "a"
常见误区与陷阱
-
混淆
void
和undefined
虽然void
允许返回undefined
,但它们是不同的概念。void
是一种类型标注,而undefined
是一个值。 -
误用
never
作为变量类型
声明一个never
类型的变量通常没有意义,因为无法为它赋值(除了通过never
函数):
let x: never; // 无法赋值
x = 42; // Error
- 忽略
never
在类型收窄中的作用
在复杂类型逻辑中,未合理使用never
可能导致类型安全性漏洞。例如:
function badTypeGuard(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase();
}
if (typeof value === "number") {
return value.toFixed(2);
}
// 这里value的类型是never,但如果没有处理,可能隐藏bug
}
高级类型系统中的交互
void
在泛型中的行为
当void
作为泛型参数时,它会被视为普通类型:
type Wrapper<T> = { value: T };
const wrappedVoid: Wrapper<void> = { value: undefined }; // 合法
never
与条件类型
在条件类型中,never
会触发分布式行为:
type Distributed<T> = T extends any ? T[] : never;
type Result = Distributed<"a" | never>; // "a"[]
函数类型兼容性
void
返回值类型在函数兼容性中有特殊规则。当一个函数类型声明为void
时,实际实现可以返回任何值(但这些值会被忽略):
type VoidFunc = () => void;
const fn: VoidFunc = () => "hello"; // 不会报错,但返回值会被忽略
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:模板字面量类型