前言
在 TypeScript(TS)中,有一种名为void的类型。它代表“空”,但需要注意的是,它与 JavaScript(JS)中的“空”并不相同。
通常,void用于声明函数的返回类型。尽管可以将变量声明为void类型,但我们一般不会这样做,因为这没有实际意义。下面通过示例来探讨原因。
void 类型
- 声明 void 类型的变量:
let name: void; // 声明一个类型为 void 的变量 name
name = 'nanjiu'; // 错误:不能将'string'类型赋值给'void'类型
name = 18; // 错误:不能将'number'类型赋值给'void'类型
name = null; // 错误:不能将'null'类型赋值给'void'类型
name = undefined; // 有效
这意味着当一个变量为void类型时,它只能被赋值为undefined,其他任何值都不被允许。
现在,你可能明白为什么我们通常不声明void类型的变量了。因为它唯一可接受的值是undefined,在实际开发中没有什么用处。
- 声明函数返回值为 void
- 显式返回:当函数的返回类型声明为
void时,可以在函数内部返回undefined:
- 显式返回:当函数的返回类型声明为
function sayHello(): void {
console.log('hello');
return undefined;
}
const str = sayHello();
console.log(str); // undefined
除了undefined,不能返回其他任何值。
- 隐式返回:在 JS 中,如果函数没有显式返回值,其隐式返回值为undefined。这使得以下代码是有效的:
function sayHello(): void {
console.log('hello');
}
const str = sayHello();
console.log(str); // undefined
不应依赖 void 值
void的另一个关键特性是,调用者不应依赖其返回值进行任何操作。例如:
let name: void; // 声明一个类型为 void 的变量 name
// 返回类型为 void 的函数
function sayHello(): void {
console.log('hello');
}
// 函数的返回值为 void,即 undefined
const str = sayHello();
console.log(str); // undefined
// 错误:不能测试"void"表达式的真假性
if (str) {
console.log('str 存在');
} else {
console.log('str 不存在');
}
因为在 TS 中void表示“空”,所以不应该用它进行任何操作。
总结:
void通常用于声明函数的返回类型,表示“空”。void类型唯一可接受的值是undefined。- 不应依赖
void类型的返回值进行任何操作。
这很简单,主要就是这两个关键点,但下面的示例可能会让你有点惊讶……
type
type是 TS 中的一个关键字,用于创建自定义类型。它允许我们为任何类型创建别名,使类型的复用和扩展更加方便。
例如:
// 创建一个可以是字符串或数字的自定义类型
type strOrnum = string | number;
// 声明一个 strOrnum 类型的变量
let str: strOrnum;
str = 'nanjiu'; // 可以赋值为字符串
str = 18; // 也可以赋值为数字
当然,type有很多强大的功能,如联合类型、交叉类型等,但这里我们不深入探讨。让我们来看一个有趣的问题:
声明函数类型
// 创建一个函数类型,参数为字符串,返回值为 void
type say = (name: string) => void;
// 定义一个 say 类型的函数
let sayHello: say = (name: string) => {
console.log(`hello ${name}`);
};
sayHello('nanjiu');
这里,我们使用type创建了一个函数类型。该函数接受一个字符串类型的参数,并返回一个void类型的值。
函数返回非 undefined 值
从上面的解释可知,返回类型为void的函数只能返回undefined(显式或隐式)。然而,在这种情况下,可以返回任何值……
// 创建一个函数类型,参数为字符串,返回值为 void
type say = (name: string) => void;
// 定义一个 say 类型的函数
let sayHello: say = (name: string) => {
console.log(`hello ${name}`);
return null;
};
const res = sayHello('nanjiu');
console.log(res); // null
为什么会这样呢?乍一看,这个示例似乎与void的定义相矛盾。这是 TS 中的一个 bug 吗?
实际上不是。官方解释是:
这种行为的存在是为了确保以下代码有效。我们知道Array.prototype.push返回一个数字,而Array.prototype.forEach期望其回调函数的返回类型为void。
const arr = [1, 2, 3, 4, 5];
const list = [0];
arr.forEach(item => list.push(item));
console.log(list);
这实际上与使用type进行自定义类型声明是一样的:
type callbackfn = (value: number, index: number, array: number[]) => void;
这个函数定义有三个参数:前两个是number类型,第三个是数字数组。函数的返回类型是void。
由于我们的回调函数使用了箭头函数简写,它隐式返回list.push(item)。然而,push方法确实有返回值。
item => list.push(item)
等价于:
item => {
return list.push(item);
}
等价于:
item => {
return 2;
// return 3;
// return 4;
//...
}
这意味着函数的返回类型变为number,与void返回类型定义不匹配。
因此,为了允许这种简写语法,TypeScript 采用了这种行为。当函数的返回类型被限制为void时,TS 不会严格强制函数不返回任何值。
否则,我们将不得不这样写:
arr.forEach(item => {
list.push(item);
});
// 或者
arr.forEach(function (item) {
list.push(item);
});
然而,尽管 TS 不严格强制void返回类型必须返回“空”,但我们仍然不能依赖其返回值进行任何操作。
// 创建一个函数类型,参数为字符串,返回值为 void
type say = (name: string) => void;
// 定义一个 say 类型的函数
let sayHello: say = (name: string) => {
console.log(`hello ${name}`);
return name;
};
const res = sayHello('nanjiu');
console.log(res); // 'nanjiu'
if (res) {
console.log('res');
}