Both of these lines complain with error "Object possibly undefined" on _e[k]
:
obj[k] = typeof _e[k] === "undefined" ? "" : _e[k].toString();
obj[k] = _e[k] === undefined ? "" : _e[k].toString();
_e[k]
is a union of many types, therefore a manual type cast is not applicable. How do I get rid of the error?
update: So in a more general manner my question can be phrased as: is there a way to narrow the type of a variable by checking against specific types which should be excluded?
minimal example:
class test {
a!: string;
b?: number;
}
const t: test = { a: "hi", b: 2 };
for (const k of Object.keys(t) as Array<keyof test>) {
console.log(t[k] == undefined ? "LOL" : t[k].toString());
}
There's a longstanding bug/issue whereby TypeScript does not perform control flow analysis to narrow the type of a property if the property key is a variable: microsoft/TypeScript#10530; it wasn't addressed because the fix imposed a large performance penalty. In your case that means type guard checks on _e[k]
will not affect subsequent uses of _e[k]
.
My recommended workaround for this is to assign your index-accessed property to a new variable and check against that:
const ek = _e[k]; // new variable
obj[k] = typeof ek === "undefined" ? "" : ek.toString(); // no error now
The new variable ek
gets normal control-flow type analysis applied, and your type guard works.
Other possibilities for different use cases:
if you've already checked against undefined
and null
but the compiler can't figure it out, the easiest change is to use the non-null assertion operator !
:
obj[k] = typeof _e[k] === "undefined" ? "" : _e[k]!.toString(); // non-null assertion
You could refactor your type-exclusion code into its own self-contained expression so that you never have to re-check the original value. For example:
obj[k] = (_e[k] || "").toString(); // refactor
That particular code only works the only falsy values _e[k]
can have are undefined
and ""
. If _e[k]
could be 0
then that would give a different result. In general you can make a new function to do whatever you want:
function defined<T, U>(x: T | undefined, dflt: U): T | U {
return typeof x !== "undefined" ? x : dflt;
}
obj[k] = defined(_e[k], "").toString(); // refactor 2
Okay, hope one of those helps; good luck!