typescriptunion-typesindex-signature

Property does not exists on type when using union with index signature


Let's assume we have following interfaces:

interface A {
  type: 'A';
  value: {
    a: 3,
  };
}

interface B {
  type: 'B';
  value: {
    b: 3,
  };
}

I create another interface which uses union of these interfaces:

interface C {
  detail: A | B;
}

Now we should be able to detect type A or B using their type property:

function test(arg: C) {
  if (arg.detail.type === 'A') {
    arg.detail.value.a;
  } else if (arg.detail.type === 'B') {
    arg.detail.value.b;
  }
}

There is no error as we expected it.

Problem: Let's make another interface which has index signature:

interface cArr {
  [index: string]: C;
}

I expect to detect type of values of cArr type same as arg variable with their type property:

function test(arg2: cArr, ID: string) {
  if (arg2[ID].detail.type === 'A') {
    arg2[ID].detail.value.a;
  }
}

But typescript gives me following error:

Property 'a' does not exist on type '{ a: 3; } | { b: 3; }'.
  Property 'a' does not exist on type '{ b: 3; }'.

What's the problem? Why if statement couldn't narrow down type of arg2?


Solution

  • TypeScript's inference is limited when it comes to indexed properties, so just store the object whose type you want to infer in a new variable:

    function test(arg2: cArr, ID: string) {
        const item = arg2[ID];
        if (item.detail.type === 'A') {
            item.detail.value.a;
        }
    }
    

    TypeScript playground