Lodash _forEach在TypeScript中打破了typeguard

时间:2017-11-16 17:21:43

标签: typescript lodash typescript2.0 union-types

背景

创建一个变量[x[0] or x[1] for x in result] ,当我没有数据时,我希望它是假的,否则就是一个对象数组。所以我做了一个联合类型:notifications很好,除非我let notifications: object[] | boolean = [];我得到了

notifications.push(notification);

是的,TS不确定它是一个数组。我会告诉它。 Property 'push' does not exist on type 'boolean | object[]'. Property 'push' does not exist on type 'true'. 同样的错误。更多的挖掘,意识到警卫工作正常,只有当我的notifications = [];内部lodash' .push我得到错误。

现状:

_forEach

我尝试了什么

一系列不同的防护方式。不明白let notifications: object[] | boolean; if (noNotifications) { notifications = false; } else { notifications = []; _forEach(notificationsObj, function (notification, type) { // Culprit notification['type'] = type; notifications.push(notification); // <-- TS error on push }); } return notifications; 打破它的原因,所以不确定下一步该尝试什么。

问题

是否可以在_forEach内的变量上使用push?如果是这样,怎么样?

2 个答案:

答案 0 :(得分:3)

TypeScript不能很好地理解_forEach,知道在运行回调中的代码之前不会重新分配notifications。由于notifications不是const,因此TypeScript认为可能可以重新分配它。 TypeScript如何进行基于类型的控制流分析有很多tradeoffs;缩小传播到闭包的类型很难。由于您无法真正让TypeScript遵循控制流程,因此您有以下几种选择:

最简单的方法就是在执行notifications时断言push()是一个数组:

_forEach(notificationsObj, function(notification, type) { // Culprit
  notification['type'] = type;
  (notifications as object[]).push(notification); // assertion
});

这个断言是你告诉编译器不要担心。

为了获得更多的类型安全性而牺牲一点运行时分配混乱,引入const变量,然后将其分配给notifications

const notificationsArray: object[] | boolean = []; // cannot be reassigned
_forEach(notificationsObj, function(notification, type) { // Culprit
  notification['type'] = type;
  notificationsArray.push(notification); // no error
});
notifications = notificationsArray; // assignment works

这里TypeScript知道永远不能重新分配notificationsArray,所以它的类型一直缩小到object[]一直到回调。然后,您可以将其值分配给notifications。 (您也可以声明notificationsArray只是输入object[],然后不要| boolean。我只是表明const会影响缩小范围。)

希望您理解并且其中一种解决方案适合您。祝你好运!

答案 1 :(得分:1)

jcalz的答案解决了您的具体问题,但我只是想提供一种解决问题的不同方法。通过以功能样式编写,您可以完全避免许多这些问题。

import * as _ from 'lodash';

function getNotifications(notificationsObj: {[type: string]: object}): object[] | boolean {
  if (_.isEmpty(notificationsObj)) {
    return false;
  } else {
    // note: lodash map will turn an object into an array
    return _.map(notificationsObj, (notification, type) => {
      return {...notification, type};
    });
  }
}

注意我也避免改变原始通知对象,与原始解决方案形成对比。如果您依赖于发生的突变,那么您应该更改该部分。