2

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?

Playground

  • what is the equals sign for at the 21st line (`curr ||= {}`) ? – Teneff Feb 18 '21 at 07:06
  • 1
    @Teneff, ehm, for the [logical OR assignment operator](https://github.com/microsoft/TypeScript/issues/31011) (or [this](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#short-circuiting-assignment-operators)) - am I missing something? – Oleg Valter is with Ukraine Feb 18 '21 at 07:15

1 Answers1

4

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).