How can I solve the error 'TS2532: Object is possibly 'undefined'?
Asked Answered
K

7

313

I'm trying to rebuild a web app example that uses Firebase Cloud Functions and Firestore. When deploying a function I get the following error:

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

This is the function:

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

I'm just trying to deploy the function to test it. And already searched the web for similar problems, but couldn't find any other posts that match my problem.

Kelleher answered 26/2, 2019 at 11:27 Comment(0)
O
361

With the release of TypeScript 3.7, optional chaining (the ? operator) is now officially available.

As such, you can simplify your expression to the following:

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

You may read more about it from that version's release notes, which cover other interesting features released on that version.

Run the following to install the latest stable release of TypeScript.

npm install typescript

That being said, Optional Chaining can be used alongside Nullish Coalescing to provide a fallback value when dealing with null or undefined values

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

Additional points:

If you are using optional chaining in the conditional if statements, you will still need to ensure that you are doing proper value/type equality checking.

The following will fail in strict TypeScript, as you are possibly comparing an undefined value with a number.

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

Instead, this is what you should be doing:

if (_?.childs && _.childs.length > 0) 
Overdress answered 15/10, 2019 at 18:55 Comment(9)
Had the issue for an array on an object even with optional chaining.Nevis
Unfortunately it's difficult to format code in comments but here it goes... I had to supply the undefined check because it would throw "Object is possibly Undefined": if (listingData && listingData.images) { image = listingData?.images[0]?.imageUrl; }Nevis
I'm thinking, is images optional field here? Thats why it could be undefined? In that case, do you wanna try this? if (listingData?.images) { image = listingData?.images?.[0].imageUrl; }Overdress
even with optional chaining I still have the error if (_?.childs?.length > 0)Basaltware
Is there any way to disable that check? I don't think it's useful in typescript at all. Update: Just add "strictNullChecks": false, to the tsconfig.json file.Hove
@Ehsansarshar apologies for the late response, but I have addressed your issue on my post in a recent updateOverdress
@Hove you might disable it, but it is still beneficial to do strict null/type checks.Overdress
@wentjuin In many cases it is not predictable, so you just have to put many question marks everywhere to remove those warnings even if it's never going to be null.Hove
I think it's much more readable to use this: if ((_?.childs?.length ?? 0) > 0) instead of using logical AND (&&) operator.Naked
F
90

Edit / Update:

If you are using Typescript 3.7 or newer you can now also do:

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

Original Response

Typescript is saying that change or data is possibly undefined (depending on what onUpdate returns).

So you should wrap it in a null/undefined check:

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

If you are 100% sure that your object is always defined then you can put this:

const data = change.after!.data();
Femoral answered 26/2, 2019 at 12:30 Comment(6)
If I do that I'll get following error: error TS7030: Not all code paths return a value. 44 .onUpdate((change, context) => Kelleher
You'll now need to return something when that first if statement returns false.Cambrai
Oh, for some reason I thought that the error related to the change and context objects. #ashamed Now it works, but my code looks very bad, because I had to add an if query for the data object as well and just added return null; in the else branches. Is there a cleaner way to implement it? #HugeDougFanKelleher
With const data = change!.after!.data() I get an error, but with const data = change.after!.data() it works. Thank you a lot. :)Kelleher
Ok, I will fix it on the answer too.Femoral
Tried ? but I got: Property 'baz' does not exist on type 'never'. for the statement: const foo = bar?.bazIlluminative
T
11

For others facing a similar problem to mine, where you know a particular object property cannot be null, you can use the non-null assertion operator (!) after the item in question. This was my code:

  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 = "";
    }
  }

And because dataToSend.naci cannot be undefined in the switch statement, the code can be updated to include exclamation marks as follows:

  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 = "";
    }
  }
Tuning answered 21/2, 2021 at 14:45 Comment(0)
B
9

One more way how can you resolve a problem:

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

But note if you use eslint you may get an error:

Forbidden non-null assertion. (eslint@typescript-eslint/no-non-null-assertion)

You can disable an error as follows:

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

But I don't advise following this way, it's better to change the eslint configuration or fix it as indicated in the documentation:

const data = change?.after?.data();
Beggary answered 10/1, 2023 at 12:43 Comment(0)
W
2

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

Worser answered 20/10, 2022 at 2:0 Comment(1)
how does this work? I'm aware that "!" can negate code, but what does that do here? (newbie question)Nonpareil
O
1

I was getting this error when I used the DocumentSnapshot type, imported from Firebase, for the docSnap (the data downloaded from the Firestore database). When I changed docSnap to any the error went away. Apparently undefined is built into the DocumentSnapshot in a way that none of the above answers can get rid of it. I tried null checks, putting in ? anywhere they'd fit, etc.

Osmen answered 20/3, 2023 at 18:40 Comment(0)
P
0

Change "strict: true" to "false" under "compilerOptions" in tsconfig.json file and restart your app.

/* To learn more about this file see: https://angular.io/config/tsconfig. */

{ "compileOnSave": false, "compilerOptions": { "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": false, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true,

Puckery answered 12/3 at 0:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.