2

Is it possible to destructure an object which comes from an function call without Typescript complaining?

File 1

  • React Component 1
...
let obj = null // <-- object initialization
...
useEffect(() => {
  obj = functionCall(...) // <-- function call that populates the object
}, [obj])
  • React component 2
const { key1, key2 } = obj // <-- object destructuring

Here I get the following complain from Typescript

  • Property 'key1' does not exist on type 'null'.
  • Property 'key2' does not exist on type 'null'.

How can I remove those warning?

eakl
  • 501
  • 8
  • 22
  • Having `obj` declared and then filled in by a `useEffect` callback looks like a mistake. It may not be one, but it looks like one. See https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron and http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call for details. Depending on where that `obj` declaration is, it's either going to be re-created every time the component function runs, or it's going to be shared between all instances of the component. Both seem problematic. – T.J. Crowder Apr 29 '21 at 09:04
  • The object is part of the global state of the app, set up as 'null' and I use useEffect to update the state with default value coming from a graphql call as those default values can be changed in the future. – eakl Apr 29 '21 at 09:07

2 Answers2

2

Specify a type for obj:

let obj: null | {key1: sometype; key2: sometype; } = null;

Note that since obj may have the value null, you'll need a guard or a default value around that destructuring assignment:

if (obj) {
    const { key1, key2 } = obj;
}

or

const { key1, key2 } = obj ?? {key1: defaultForKey1, key2: defaultForKey2};

or

const { key1 = defaultForKey1, key2 = defaultForKey2 } = obj ?? {};

The difference between the last two is what happens if obj isn't null but key1 or key2 has the value undefined (if it's allowed to by the types).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

useEffect runs after your initial render - so on the first render obj will be null, and so TS is right to complain.

You need to check that obj is not null before de-structuring. Also, give it a type e.g

type MyType = { key1: string; key2: number; }; // for example

let obj: MyType | null  = null;

if (obj) {
  const { key1, key2 } = obj; // OK
} else {
  // In here, obj is null
}

mbdavis
  • 3,861
  • 2
  • 22
  • 42