typescripttypescript-typingsoptional-chainingindex-signature

Optional chaining for interfaces that have an index signature


I defined two interfaces. The first one has an optional field, the second one has an index signature:

interface A {
  foo?: { bar: number };
}

interface B {
  [s: string]: { bar: number };
}

Why does the first interface give me a result of type number | undefined when I access the property using optional chaining, whereas the second one gives only type number?

const a: A = {};
const aa = a.foo?.bar;
// const aa: number | undefined

const b: B = {};
const bb = b.foo?.bar;
// const bb: number

Solution

  • Because B is indexed by [s: string], it believe that any valid string key will result in the value of { bar: number } being accessed. So, with

    const b: B = {};
    const bb = b.foo?.bar;
    

    it thinks that the foo property will always exist - it'll never fail and turn into undefined via optional chaining.

    In contrast, with A's:

    interface A {
      foo?: { bar: number };
    }
    

    Since the foo property is optional here, it's not sure to exist (to Typescript), so the ?. may result in no property being found and undefined being the result.

    You can make it clear that not all properties on B will necessarily have the bar object by alternating with undefined in the object value type:

    interface B {
      [s: string]: { bar: number } | undefined;
    }
    

    resulting in bb being typed as number | undefined.