Description
TypeScript Version: 3.8.3 vs nightly
Search Terms: intersection in operator
Code
type MaybeFoo = {
bar: string;
foo?: number;
};
type AlwaysFoo = {
foo: number | undefined;
always: string;
};
declare const f: AlwaysFoo & MaybeFoo;
if ("foo" in f) {
f.bar;
} else {
// 3.8.3: f: AlwaysFoo & MaybeFoo
// 3.9: 'f' is 'never'
f.bar;
}
Expected behavior: Well...
This is probably technically the correct behavior. However, for the specific case of window
, this is problematic. There are globals like ontouchstart
which are declared as being var ontouchstart: whatever | undefined
in the global scope; these get intersected with the global Window
type, and because we believe "variables can't be optional", we start narrowing to never
in the else branch here.
It's unclear what we should do differently here, but this caused a break in a few different places in real world code where people are doing feature detection. We're incorrectly telling people that e.g. ontouchstart
is always present, even though you would get an error to unconditionally invoke it (because it is indeed possibly-undefined
).
Since there's still no "optional var" and still no "missing vs undefined", we should maybe treat properties that include undefined as "possibly not in
" for the purposes of narrowing.
Actual behavior: Described
Playground Link: Link
Related Issues: Possible root cause: #37106