为什么 TypeScript 有 `void` 和 `undefined`?

Rya*_*ugh 37 undefined void typescript

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

function fn1(): void {
  // OK
}

function fn2(): void {
  // Error
  return 3;
}
Run Code Online (Sandbox Code Playgroud)

您还可以注释要返回的函数undefined

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

function fn4(): undefined {
  // Error
  return 3;
}
Run Code Online (Sandbox Code Playgroud)

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

function fn5(): void {
}
let u: undefined = fn5(); // Error
Run Code Online (Sandbox Code Playgroud)

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

Rya*_*ugh 64

void在函数返回类型中具有特殊含义,并且不是undefined. 这样的它的思考是非常错误的。为什么?

的目的void不会观察到函数的返回值。这与will beundefined非常不同。有这种区别很重要,这样你就可以正确地描述像forEach. 让我们考虑一个独立版本的Array#forEach,用undefined而不是void在回调返回位置编写:

declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void;
Run Code Online (Sandbox Code Playgroud)

如果您尝试使用此功能:

let target: number[] = [];
forEach([1, 2, 3], el => target.push(el));
Run Code Online (Sandbox Code Playgroud)

你会得到一个错误:

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

这是一个正确的错误——你说你想要一个返回值的函数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));
Run Code Online (Sandbox Code Playgroud)

为什么不直接使用any?如果您实际上是实现 的人forEach,那么您真的不希望那样 -any浮动是一件危险的事情,可以很容易地击败类型检查。

其推论是,如果您有某个返回类型为 的函数表达式void则不能肯定地说调用该函数的结果是undefined

再次,void为别名undefined和类型的表达式void可以具有任何值,而不仅仅是undefined

在返回类型明确列为的函数void,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;
  };
}
Run Code Online (Sandbox Code Playgroud)

  • 那么“void”和“unknown”有什么区别呢?我认为“未知”类型是“有任何输入,没有任何输出”类型?- 非常适合未观察到的函数返回类型。 (5认同)
  • 作为推论,这是否意味着可以选择返回某个值(比方说一个数字)的回调应该声明为返回 `number | 未定义`而不是`number | 无效`? (4认同)
  • 很好的解释,谢谢!我仍然有一个问题是:如果我们根本不应该信任 void 函数的返回值,为什么 TS 允许分配 void 函数的返回值?`函数 b(): void {}; 常量 y = b()` (2认同)
  • 不幸的是,似乎 TypeScript *有时*确实将 void 视为未定义,这可能会导致麻烦:https://gist.github.com/rkjnsn/0435efb3af33d74b06d337f0f2706224 (2认同)
  • 考虑到它将编译到的 JS 运行时,这一切都感觉太理论化并且具有误导性。 (2认同)