为什么 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是形参符号,可以绑定数值或未提供的值。