【问题标题】:Why does TypeScript have both `void` and `undefined`?为什么 TypeScript 既有 `void` 又有 `undefined`?
【发布时间】:2020-03-12 02:33:01
【问题描述】:

在 TypeScript 中,您可以将函数注释为返回 void

function fn1(): void {
  // OK
}

function fn2(): void {
  // Error
  return 3;
}

你也可以注释一个函数来返回undefined

function fn3(): undefined {
  // OK
  return;
}

function fn4(): undefined {
  // Error
  return 3;
}

所以看起来如果你调用一个返回void的函数,你总是会得到undefined的值。但是你不能写这段代码:

function fn5(): void {
}
let u: undefined = fn5(); // Error

为什么void 不只是undefined 的别名?它需要存在吗?

【问题讨论】:

    标签: typescript undefined void


    【解决方案1】:

    void 在函数返回类型中有特殊含义,不是undefined 的别名。这样想是非常错误的。为什么?

    void 的意图是函数的返回值不会被观察到。这与 将是 undefined 非常不同。有这种区别很重要,这样您就可以正确描述像forEach 这样的函数。让我们考虑一个独立版本的Array#forEach,在回调返回位置使用undefined 而不是void

    declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void;
    

    如果您尝试使用此功能:

    let target: number[] = [];
    forEach([1, 2, 3], el => target.push(el));
    

    你会得到一个错误:

    类型“数字”不能分配给类型“未定义”

    这是一个正确的错误 - 你说你想要一个返回值 undefined 的函数,但你实际上提供了一个返回值 number 的函数,因为这是 Array#push 返回的值!

    使用void 代替意味着forEach 承诺不使用返回值,因此可以使用返回任何值的回调来调用它

    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 浮动是一件很危险的事情,很容易破坏类型检查。

    由此得出的结论是,如果您有某个返回类型为void 的函数表达式,您无法确定调用该函数的结果是undefined。 p>

    同样,void 不是 undefined 的别名,void 类型的表达式可能有 any 值,而不仅仅是 undefined

    在返回类型明确列为void 的函数body 中,TypeScript 将阻止您“意外”返回值,即使这不会造成类型系统违规。这有助于捕获重构中出现的错误:

    // 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;
      };
    }
    

    【讨论】:

    • 很好的解释,谢谢!我仍然有一个问题是:如果我们根本不应该信任来自 void 函数的返回值,为什么 TS 允许分配 void 函数的返回值? function b(): void {}; const y = b()
    • 作为推论,这是否意味着可以选择返回某个值(比如说一个数字)的回调应该声明为返回 number | undefined 而不是 number | void
    • 那么voidunknown有什么区别呢?我认为unknown 类型是“有进有出”的类型? - 非常适合未观察到的函数返回类型。
    • 不幸的是,TypeScript sometimes 似乎确实将 void 视为未定义,这可能会导致麻烦:gist.github.com/rkjnsn/0435efb3af33d74b06d337f0f2706224
    【解决方案2】:

    它们在语义上是不同的。

    • undefined 是限制符号的形容词。
      • “符号未定义”表示“此符号未绑定到任何值”。
      • 你不能说“一个值是未定义的”。
    • void 是限制价值的形容词。
      • “a value is void”表示“未提供此值”。
      • 您不能说“未提供符号”。

    示例 1:

    • ✔️function f(): void {}

      f 是一个返回未提供值的函数。

    • function f(): undefined {}

      f 是一个返回未定义值的函数。

    示例 2:

    • ✔️const a: void = &lt;void&gt;f();

      a 是一个可以绑定到未提供值的符号。

    • const a: undefined = &lt;void&gt;f();

      a 是一个可以绑定到未定义值的符号。

    示例 3:

    • ✔️const a: void = &lt;void&gt;f();

      断言f()的返回值是一个未提供的值。

    • const a: void = &lt;undefined&gt;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 是一个形参符号,可以绑定到数值或未提供的值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-16
      • 1970-01-01
      • 2011-06-06
      • 2012-06-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多