我试图重建一个使用Firebase云函数和Firestore的web应用程序示例。当部署一个函数时,我得到以下错误:

src/index.ts:45:18 - error TS2532: Object is possibly 'undefined'.
45     const data = change.after.data();

这是函数:

export const archiveChat = functions.firestore
  .document("chats/{chatId}")
  .onUpdate(change => {
    const data = change.after.data();

    const maxLen = 100;
    const msgLen = data.messages.length;
    const charLen = JSON.stringify(data).length;

    const batch = db.batch();

    if (charLen >= 10000 || msgLen >= maxLen) {

      // Always delete at least 1 message
      const deleteCount = msgLen - maxLen <= 0 ? 1 : msgLen - maxLen
      data.messages.splice(0, deleteCount);

      const ref = db.collection("chats").doc(change.after.id);

      batch.set(ref, data, { merge: true });

      return batch.commit();
    } else {
      return null;
    }
  });

我只是想部署这个函数来测试它。我已经在网上搜索了类似的问题,但找不到任何其他与我的问题相匹配的帖子。


编辑/更新:

如果你使用的是Typescript 3.7或更新版本,你现在也可以这样做:

    const data = change?.after?.data();

    if(!data) {
      console.error('No data here!');
       return null
    }

    const maxLen = 100;
    const msgLen = data.messages.length;
    const charLen = JSON.stringify(data).length;

    const batch = db.batch();

    if (charLen >= 10000 || msgLen >= maxLen) {

      // Always delete at least 1 message
      const deleteCount = msgLen - maxLen <= 0 ? 1 : msgLen - maxLen
      data.messages.splice(0, deleteCount);

      const ref = db.collection("chats").doc(change.after.id);

      batch.set(ref, data, { merge: true });

      return batch.commit();
    } else {
      return null;
    }

原始响应

Typescript是说更改或数据可能是未定义的(取决于onUpdate返回什么)。

所以你应该把它包装在一个null/undefined检查:

if(change && change.after && change.after.data){
    const data = change.after.data();

    const maxLen = 100;
    const msgLen = data.messages.length;
    const charLen = JSON.stringify(data).length;

    const batch = db.batch();

    if (charLen >= 10000 || msgLen >= maxLen) {

      // Always delete at least 1 message
      const deleteCount = msgLen - maxLen <= 0 ? 1 : msgLen - maxLen
      data.messages.splice(0, deleteCount);

      const ref = db.collection("chats").doc(change.after.id);

      batch.set(ref, data, { merge: true });

      return batch.commit();
    } else {
      return null;
    }
}

如果你100%确定你的对象总是有定义的,那么你可以这样写:

const data = change.after!.data();

随着TypeScript 3.7的发布,可选的链接(?Operator)现在正式可用。

因此,您可以将表达式简化为以下形式:

const data = change?.after?.data();

您可以从该版本的发布说明中了解到更多信息,其中包括该版本发布的其他有趣功能。

运行以下命令,安装最新稳定版本的TypeScript。

npm install typescript

也就是说,可选链接可以与Nullish Coalescing一起使用,以在处理null或未定义值时提供一个回退值

const data = change?.after?.data() ?? someOtherData();

附加分:

如果你在条件If语句中使用可选链接,你仍然需要确保你做了正确的值/类型相等性检查。

下面的代码在严格的TypeScript中会失败,因为你可能在比较一个未定义的值和一个数字。

if (_?.childs?.length > 0) 

相反,你应该这样做:

if (_?.childs && _.childs.length > 0) 

对于其他面临与我类似问题的人(知道特定对象属性不能为空),可以在相关项后使用非空断言操作符(!)。 这是我的代码:

  const naciStatus = dataToSend.naci?.statusNACI;
  if (typeof naciStatus != "undefined") {
    switch (naciStatus) {
      case "AP":
        dataToSend.naci.certificateStatus = "FALSE";
        break;
      case "AS":
      case "WR":
        dataToSend.naci.certificateStatus = "TRUE";
        break;
      default:
        dataToSend.naci.certificateStatus = "";
    }
  }

因为dataToSend。Naci不能在switch语句中未定义,代码可以更新为包含感叹号,如下所示:

  const naciStatus = dataToSend.naci?.statusNACI;
  if (typeof naciStatus != "undefined") {
    switch (naciStatus) {
      case "AP":
        dataToSend.naci!.certificateStatus = "FALSE";
        break;
      case "AS":
      case "WR":
        dataToSend.naci!.certificateStatus = "TRUE";
        break;
      default:
        dataToSend.naci!.certificateStatus = "";
    }
  }

Const data = change!.after!.data();


还有一种解决问题的方法:

const data = change!.after!.data();

但是注意,如果你使用eslint,你可能会得到一个错误:

禁止非空断言。 (eslint@typescript-eslint / no-non-null-assertion)

您可以通过以下方法禁用错误:

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const data = change!.after!.data();

但我不建议这样做,最好是修改eslint配置或按照文档中的说明进行修复:

const data = change?.after?.data();