为什么 TypeScript 同时具有 `void` 和 `undefined`?
Why does TypeScript have both `void` and `undefined`?
在 TypeScript 中,您可以将函数注释为 returning void
:
function fn1(): void {
// OK
}
function fn2(): void {
// Error
return 3;
}
你也可以给函数注解 return undefined
:
function fn3(): undefined {
// OK
return;
}
function fn4(): undefined {
// Error
return 3;
}
因此,如果您 return 调用 void
函数,您似乎总是会得到值 undefined
。但是你不能写这个代码:
function fn5(): void {
}
let u: undefined = fn5(); // Error
为什么 void
不只是 undefined
的别名?它是否需要存在?
void
在函数return类型中有特殊含义,不是undefined
的别名。这样想是非常错误的。为什么?
void
的目的是函数的 return 值 不会被观察到 。这与 将 undefined
大不相同。具有这种区别很重要,这样您才能正确描述 forEach
之类的函数。让我们考虑 Array#forEach
的独立版本,在回调 return 位置用 undefined
而不是 void
编写:
declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void;
如果您尝试使用此功能:
let target: number[] = [];
forEach([1, 2, 3], el => target.push(el));
你会得到一个错误:
Type "number" is not assignable to type "undefined"
这是一个正确的错误 - 你说你想要一个 returned 值 undefined
的函数,但你实际上提供了一个 returned 值 [=23] 的函数=] 因为这就是 Array#push
returns!
改为使用 void
意味着 forEach
承诺不使用 return 值,因此可以使用 return 任何值的回调来调用它
declare function forEach<T>(arr: T[], callback: (el: T) => void): void;
let target: number[] = [];
// OK
forEach([1, 2, 3], el => target.push(el));
为什么不直接使用 any
呢?如果您实际上是实施 forEach
的人,您真的不希望如此 - 浮动 any
是一件危险的事情,可以很容易地击败类型检查。
由此得出的结论是,如果你有一些 return 类型为 void
的函数表达式, 你不能肯定地说调用该函数的结果是undefined
.
同样,void
不是 undefined
的别名,void
类型的表达式可能有 any 值,不仅仅是 undefined
在函数 body 中,其 return 类型明确列为 void
,TypeScript 将阻止您“意外地” returning一个值,即使这不会造成类型系统冲突。这有助于捕获重构中出现的错误:
// Old version
function fn(arr: number[]): void {
const arr1 = arr.map(x => {
return 3;
});
}
// New version
function fn(arr: number[]): void {
for (const x of arr) {
// Oops, meant to do something else
return 3;
};
}
它们在语义上是不同的。
undefined
是形容词符号。
- “符号未定义”表示“此符号未绑定任何值”。
- 你不能说“一个值是未定义的”。
void
是一个形容词,限制了 值 。
- "a value is void" 表示"未提供此值"。
- 你不能说“未提供符号”。
示例 1:
✔️function f(): void {}
f
是一个 returns 未提供值的函数。
❌function f(): undefined {}
f
是一个 returns 一个 未定义值 .
的函数
示例 2:
✔️const a: void = <void>f();
a
是一个可以绑定到未提供值的符号。
❌const a: undefined = <void>f();
a
是一个可以绑定到 未定义值 .
的符号
示例 3:
✔️const a: void = <void>f();
断言 f()
的返回值是一个未提供的值。
❌const a: void = <undefined>f();
断言 f()
的返回值是一个 未定义的值 。
示例 4:
❌function g(x?: number) { assert(x === undefined); }
x
是一个绑定到 未定义值 .
的符号
✔️function g(x?: number) { assert(typeof x === 'undefined'); }
x
是一个不绑定任何值的符号。
❌function g(x?: number) { assert(typeof x === 'void'); }
x
是 未提供的符号 。
示例 5:
❌function h(y: undefined | number) { }; h();
y
是一个形式参数符号,可以绑定到一个数值或未定义的值.
✔️function h(y: void | number) { }; h()
y
是形参符号,可以绑定数值或未提供的值。
✔️function h(y?: number) { }; h()
y
是形参符号,可以绑定数值或未提供的值。
在 TypeScript 中,您可以将函数注释为 returning void
:
function fn1(): void {
// OK
}
function fn2(): void {
// Error
return 3;
}
你也可以给函数注解 return undefined
:
function fn3(): undefined {
// OK
return;
}
function fn4(): undefined {
// Error
return 3;
}
因此,如果您 return 调用 void
函数,您似乎总是会得到值 undefined
。但是你不能写这个代码:
function fn5(): void {
}
let u: undefined = fn5(); // Error
为什么 void
不只是 undefined
的别名?它是否需要存在?
void
在函数return类型中有特殊含义,不是undefined
的别名。这样想是非常错误的。为什么?
void
的目的是函数的 return 值 不会被观察到 。这与 将 undefined
大不相同。具有这种区别很重要,这样您才能正确描述 forEach
之类的函数。让我们考虑 Array#forEach
的独立版本,在回调 return 位置用 undefined
而不是 void
编写:
declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void;
如果您尝试使用此功能:
let target: number[] = [];
forEach([1, 2, 3], el => target.push(el));
你会得到一个错误:
Type "number" is not assignable to type "undefined"
这是一个正确的错误 - 你说你想要一个 returned 值 undefined
的函数,但你实际上提供了一个 returned 值 [=23] 的函数=] 因为这就是 Array#push
returns!
改为使用 void
意味着 forEach
承诺不使用 return 值,因此可以使用 return 任何值的回调来调用它
declare function forEach<T>(arr: T[], callback: (el: T) => void): void;
let target: number[] = [];
// OK
forEach([1, 2, 3], el => target.push(el));
为什么不直接使用 any
呢?如果您实际上是实施 forEach
的人,您真的不希望如此 - 浮动 any
是一件危险的事情,可以很容易地击败类型检查。
由此得出的结论是,如果你有一些 return 类型为 void
的函数表达式, 你不能肯定地说调用该函数的结果是undefined
.
同样,void
不是 undefined
的别名,void
类型的表达式可能有 any 值,不仅仅是 undefined
在函数 body 中,其 return 类型明确列为 void
,TypeScript 将阻止您“意外地” returning一个值,即使这不会造成类型系统冲突。这有助于捕获重构中出现的错误:
// Old version
function fn(arr: number[]): void {
const arr1 = arr.map(x => {
return 3;
});
}
// New version
function fn(arr: number[]): void {
for (const x of arr) {
// Oops, meant to do something else
return 3;
};
}
它们在语义上是不同的。
undefined
是形容词符号。- “符号未定义”表示“此符号未绑定任何值”。
- 你不能说“一个值是未定义的”。
void
是一个形容词,限制了 值 。- "a value is void" 表示"未提供此值"。
- 你不能说“未提供符号”。
示例 1:
✔️
function f(): void {}
f
是一个 returns 未提供值的函数。❌
function f(): undefined {}
的函数f
是一个 returns 一个未定义值.
示例 2:
✔️
const a: void = <void>f();
a
是一个可以绑定到未提供值的符号。❌
const a: undefined = <void>f();
的符号a
是一个可以绑定到未定义值.
示例 3:
✔️
const a: void = <void>f();
断言
f()
的返回值是一个未提供的值。❌
const a: void = <undefined>f();
断言
f()
的返回值是一个未定义的值。
示例 4:
❌
function g(x?: number) { assert(x === undefined); }
的符号x
是一个绑定到未定义值.✔️
function g(x?: number) { assert(typeof x === 'undefined'); }
x
是一个不绑定任何值的符号。❌
function g(x?: number) { assert(typeof x === 'void'); }
x
是未提供的符号。
示例 5:
❌
function h(y: undefined | number) { }; h();
y
是一个形式参数符号,可以绑定到一个数值或未定义的值.✔️
function h(y: void | number) { }; h()
y
是形参符号,可以绑定数值或未提供的值。✔️
function h(y?: number) { }; h()
y
是形参符号,可以绑定数值或未提供的值。