2

I want to loop through an array of objects in js to find the element with a specific key.

It's worth noting that, the 'specific key' will exist in only one of the objects and no more. (it is also possible the 'key' will not exist in any of the objects)

For Example:

const arr: [
      { foo: number; fooo: number },
      { bar: number; barr: number },
      { baz: number; bazz: number }
    ] = [
      { foo: 100, fooo: 1 },
      { bar: 3, barr: 200 },
      { baz: 0, bazz: 0 },
    ];

I am using below code to find the object which has the wanted key:

const wantedObj = arr.find((el) => (typeof el.baz !== 'undefined'))

Since it is possible for the key to have a falsey value (ex: 0 ), I am checking (typeof el.baz !== 'undefined') condition.

But I get the TS error of

Property 'bazz' does not exist on type '{ foo: number; fooo: number; } | { bar: number; barr: number; } | { baz: number; bazz: number; }'.
Property 'bazz' does not exist on type '{ foo: number; fooo: number; }'.ts(2339)

Why is TS giving this error while I have obviously defined the type of arr? and how to fix it?

2
  • Because typescript does not think that you have defined your types correctly. As you can see in the error, bazz does not exist on { foo: number; fooo: number; }. Elements in arr could be of type { foo: number; fooo: number; }. One way to handle that would be to use type guards. Also see narrowing Commented Nov 10, 2022 at 12:38
  • Does this answer your question? Typescript union of Array and Object types Commented Nov 10, 2022 at 12:42

1 Answer 1

3

Not every object in arr has a baz property. That's why the type of el inside the find looks like this:

{
    foo: number;
    fooo: number;
} | {
    bar: number;
    barr: number;
} | {
    baz: number;
    bazz: number;
}

Whenever you have a union of different object types, you can only access shared properties with the dot-notation.

We have to narrow the type of el first before accessing baz. The in operator let's us check an object for any property. Even those which are not defined in its type. The in operator also lets us narrow the union to those elements where the property exists.

const wantedObj = arr.find((el): el is Extract<typeof el, { baz: any }> => 
  "baz" in el && typeof el.baz !== "undefined"
);

wantedObj?.baz

Playground

Sign up to request clarification or add additional context in comments.

4 Comments

If we narrow it down with "baz" in el, then there is no need to check the second condition. right? (removing the second condition doesn't change anything i think)
You should keep it. baz could still be set with undefined. "baz" in el would return true, even if it is undefined.
Can't TS infer the type of wantedObj? In the playground link, I am still getting error on accessing wantedObj?.baz
@AliBaghban - See my edit. You are gonna need a type predicate. Without a type predicate, find just returns the same type as the source array. The type predicate tells TypeScript to modify the type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.