#Checking if arbitrary string property exists in object using bracket notation

1 messages · Page 1 of 1 (latest)

digital condorBOT
#
pcpw#0

Preview:```ts
const someObj = {
a: "foo",
b: "bar",
}

function doSomething(str: string) {
if (str in someObj) {
// Typescript is not happy
console.log(someObj[str])
}

// Typescript is not happy
if (typeof someObj[str] !== "undefined") {
}
}```

craggy bobcat
#

If you want to treat an object like a arbitrary 'dictionary' you can type it as one:

const someObj: Record<string, string | undefined> = {
#

(If you have noUncheckedIndexedAcccess enabled, the | undefined is unnecessary)

undone mauve
#

Is there a way to check if a property exists in an object literal without typing it as an arbitrary dictionary?

craggy bobcat
#

If the thing you're checking is a known key, then yes.

#

e.g.

function doSomething(str: "a" | "b") {
  const val = someObj[str];
  if(val !== undefined) {
    console.log(val.toUpperCase())
  }
}
#

(... but this case doesn't make much sense because it'll always have those keys)

undone mauve
#

So it's impossible if I'm checking it against an arbitrary string? The object has dozens of properties and it's impractical to check my string against a dozen keys during runtime

#

I can cast it as keyof typeof someObj but that's just dirty

craggy bobcat
#

Yeah, you either know what the possible keys are or you don't.

#

And if you're checking an arbitrary string, TS can't know what the type is anyway.

#

Since objects can have excess properties that TS doesn't know about.

undone mauve
#

Why can't TS narrow str to keyof someObj with the condition if (str in someObj) {}?

#

If the condition holds true, then str must be a | b

craggy bobcat
#

Because keyof someObj is the known keys of the object, but str could be an unknown key.

undone mauve
#

str being an unknown key as in another key in the prototype chain?

craggy bobcat
#

Or just a normal key that TS doesn't know about: in this case your object being a literal constant makes that unlikely, but objects aren't always literals.

#

!*:unsafe-keys-example

digital condorBOT
#
retsam19#0
`!retsam19:unsafe-keys-example`:

An example where assuming Object.keys(x) returns keyof (typeof x) is unsafe:

type XY = {
   x: string,
   y: string,
}
function louder(obj: XY) {
    Object.keys(obj).forEach(key => {
        // If key is "x" | "y", then this compiles:
        console.log(obj[key].toUpperCase());
    })
}
const obj = { x: "x", y: "y", zero: 0};
louder(obj); // compiles, because extra properties are allowed... but crashes at runtime
craggy bobcat
#

This is illustrating a somewhat different, but related problem, but it's the same idea. keyof XY is "x" | "y", but that doesn't mean those are the only keyes the object actually has.

undone mauve
#

That makes sense. Thanks

craggy bobcat
#

Adapting for your example:

function doSomething(someObj: {a: string, b: string}, str: string) {
  if (str in someObj) {
    console.log(someObj[str]);
  } 
}

const someObj = {
  a: 'foo',
  b: 'bar',
  zero: 0,
};

// Type-checks: runtime errors
doSomething(someObj, 'zero');
#

(It would be nice if TS carved out an exception for object literals)