TypeScript Version: 4.0.3
Search Terms: "Object is possibly 'undefined' map"
Code
type Thing = { data: any };
const things: Map<string, Thing> = new Map();
function add_thing (id: string, data: any) {
let thing = things.get(id);
if (typeof thing === 'undefined') {
thing = { data };
things.set(id, thing);
}
return () => thing.data;
}
Expected behavior:
No errors - it is not possible for "thing" to be undefined based on the above code.
Actual behavior:
Object is possibly 'undefined' error.
If things is instead defined as const things = new Map(); then there is no error.
If it is assigned to another constant like this const thing2 = thing; and that is used in the closure then there is also no error.
Playground Link: https://www.typescriptlang.org/play?#code/C4TwDgpgBAKgFgSwHYHMoF4oG8oBMCGw+AXFPkiFAL4DcAUHQMYD2SAzsFMIqm6QLL4wAHg4AnZCgA0sHigB8GKEggB3KILAAKAJT06AMwCuSRsASsyuXAH1ukqFoS5S4yTIJFS5EDux0oQKgAGwhOe1QlCJQ2ADoUMKdcPQYgqAQDR1BIZkzojHRMAHITXAgDZAhcIr8sALSg-MwcT3xqegbGuTi2ROcZaJS0qlSgsTCjMSRHP3RFaNjW+iogA
Related Issues: #7719
Duplicate of #9998.
Quick workaround: Store it in a new const variable:
const cthing = thing;
return () => cthing.data;
@MartinJohns thanks I am aware of this workaround - edited the description.
It's still a duplicate of #9998. :-)
If
thingsis instead defined asconst things = new Map();then there is no error.
If you declare it like this, then the type is inferred to be Map<any, any>. As a result the type of thing is any, and you opted out of all type-safety checks. That's why you don't get an error in this case.
Another nasty workaround:
const thing = things.get(id) ?? (things.set(id, { data }), things.get(id)!);
return () => thing.data;
(Please don't use this.)
I guess my objection to 9998 is the "discussion" label there when this is clearly a bug IMHO. That and my example looks way more RWC to me than the code in that ticket, although I did simplify it a bit for brevity. ;-)
The least ugly workaround seems to be this: return () => thing!.data;
That's why we need Map.prototype.emplace. You might consider using a polyfill of it.
function add_thing (id: string, data: any) {
return things.emplace(id, {insert: () => ({ data })});
}
This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.