MrMamen
MrMamen

Reputation: 389

Check for optional properties in typescript

Consider the following interfaces

interface Foo {
  bar?: Bar;
}

interface Bar {
  baz?: Baz;
}

interface Baz {
  foobar: number;
}

I would assume that this is a safe way access data:

const test = (foo: Foo) => {
  const baz = (foo.bar || {}).baz || {};
  const foobar = baz.foobar;
};

However, it results in Error:(298, 26) TS2339: Property 'foobar' does not exist on type '{}'.

OK. But then I would not assume that this gives a different result:

const test = (foo: Foo) => {
  const baz = (foo.bar || {}).baz;
  const foobar = ({} || baz).foobar;
};

While this is a workaround, I'm not very happy with it, as I'm using baz a lot for more properties. To me this seems like a typescript bug.

Upvotes: 0

Views: 1410

Answers (1)

Tim Perry
Tim Perry

Reputation: 13276

I'm not totally clear about your examples (they both seem to fail in quite similar ways). It doesn't matter too much though; right now, the best way to do the kind of nested optional property lookup you're trying to do here is something like this:

const test = (foo: Foo) => {
  const foobar = foo.bar && foo.bar.baz && foo.bar.baz.foobar;
};

It'll work, but it's not pretty. There are a few libraries available to make this nicer though, e.g. typesafe-get:

const test = (foo: Foo) => {
  const foobar = get(foo, 'bar', 'baz', 'foobar');
};

In the medium term though, the real solution is optional chaining. This is a new JavaScript feature, which should be landing in TypeScript 3.7 this November. That'll look like this:

const test = (foo: Foo) => {
  const foobar = foo?.bar?.baz?.foobar;
};

Upvotes: 1

Related Questions