#"in" operator to safely access object property

30 messages · Page 1 of 1 (latest)

wicked crater
#

I am struggling to fix this error. Can someone help me understand why Typescript raises an error here ?

neat nightBOT
#
WizZ#1874

Preview:ts type A = { a: string } type B = { b: string } type C = { c: string } const keys = ["a", "b", "c"] as const function myFunction(obj: A | B | C) { for (const [index, key] of keys.entries()) { if (key in obj) { console.log(obj[key]) // ^Element implicitly has an 'any' type because expression of type '"a" | "b" | "c"' can't be used to index type 'A | B | C' ...

placid spear
#

well, obj could be of type A, which means it would only have the a property

#

but then you'd try to access the b and c property using the array

#

which isn't allowed

wicked crater
#

If obj is of type A and key is "a" the condition is false and I will not access the property I think ?

placid spear
#

yeah, but TS doesn't see it that way

#

for TS obj is of type A | B | C

#

and you can't use a key to access a property when you aren't sure it's in that object

#

what you ned to do is use a type predicate

#

so you can narrow key

#

and you'll be sure it will be a key of obj

wicked crater
#

With type guards do I have to do 3 separate cases here ?

placid spear
#

not really

#

you can simply move the chec in a type predicate function and use that function

neat nightBOT
#
Ascor8522#7606

Preview:```ts
type A = {
a: string
}

type B = {
b: string
}

type C = {
c: string
}

const keys = ["a", "b", "c"] as const

function myFunction(obj: A | B | C) {
for (const [index, key] of keys.entries()) {
if (isKey(obj, key)) {
console.log(obj[key])
...```

wicked crater
#

Thanks a lot i was about to do something really dirty 😆
Was about to use this :

type ABC = {
  a: string
  b: string
  c: string
}
function objHasKey(obj: A | B | C, key: Key): obj is ABC{
  return key in obj;
}
placid spear
#

yeah, that work too

#

wait, no

#

why did you merge all the types together into one?

wicked crater
#

Because I didn't think of k is keyof typeof o to narrow 🙂

#

I was trying to narrow obj instead of key 🙃

#

I have an error still !
With your example key is typed as never

placid spear
#

oh, right

#

whoops

safe fog
#

@wicked crater One simple thing you can do here is "upcast" and just make the object a Record<string, string> that you can access (and null check)

#
function myFunction(obj: A | B | C) {
  // Partial can be omitted with --noUncheckedIndexedAccess enabled
  const _obj: Partial<Record<string, string>> = obj;
  for (const [index, key] of keys.entries()) {
    const val = _obj[key];
    if(val) {
      console.log(val);
    }
  }
}
#

Or if you want to be more precise:

#
const _obj: Partial<Record<"a" | "b" | "c", string>> = obj;
wicked crater
#

@safe fog thanks for the hint I adopted the upcast, it's still better than using @ts-ignore or a cast 😊