Reputation: 389
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
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