Reputation: 5835
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.
Upvotes: 329
Views: 702580
Reputation: 61
My solution was to enclose it with if check. obj!.fld is better but didn't work.
if (obj.fld) {
somefunc(obj.fld);
}
Upvotes: 0
Reputation: 47
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,
Upvotes: -1
Reputation: 10930
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.
Upvotes: 1
Reputation: 420
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();
Upvotes: 10
Reputation: 42566
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)
Upvotes: 376
Reputation: 2840
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 = "";
}
}
Upvotes: 11
Reputation: 7015
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;
}
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();
Upvotes: 91