The following is an example that reproduces the problem:
type F = () => number;
type R = {
[ x: string ]: R | F | undefined
}
const isFunc = <T extends (...args: any[]) => any>(maybe:unknown) : maybe is T => typeof maybe === "function";
const check = (i: R) => {
let tmp:R = i;
let curr = tmp["something"];
if( isFunc<F>(curr) ) return;
curr // R | undefined, expected
tmp = curr || (curr = {}); //ok, expected
tmp = curr ||= {}; //Index signature is missing in type 'F'
};
As you can see, after the type guard, curr
is correctly narrowed to R | undefined
. After that, I am reassigning tmp
to curr
and defaulting the latter to an empty object should it be missing.
Now, if the A || A = B
approach is used, curr
on the left side of the logical OR is properly narrowed to R | undefined
. However, if I use the logical OR assignment to make the intent clearer, curr
is inferred as R | F | undefined
. This obviously results in an error, since F
is not assignable to R
.
The question is - what would be the reason for curr
to be losing the narrowing in the second case?
After submitting this as an issue in the source repository, the behavior has been confirmed to be a bug (more like a design limitation because no control-flow analysis is done when using logical assignment operators as opposed to short-circuiting assignment with logical operators).