How to dynamically access object property in TypeScript

Viewed 27724

I've been going through and trying to convert existing projects (from Node.js) to TypeScript.

For context, I'm using the http-status package (https://www.npmjs.com/package/http-status)

I'm trying to pass variables through into their default export, but it's getting an error:

import status = require("http-status");

status.OK; // this works
status["OK"] // this also works

let str = "OK";
status[str]; // error

Error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'HttpStatus'.
No index signature with a parameter of type 'string' was found on type 'HttpStatus'.


How would I convert this usage to TypeScript?

4 Answers

"OK" is a string, and str is implicitly taking the type string in your code.

When you try to access an object's property, you need to use a type keyof. TypeScript then knows you are not assigning a random string; you are assigning strings compatible with the properties (keys) for the object.

Also, since status is a variable, not a type, you need to extract its type with typeof.

Try:

let str = "OK" as keyof typeof status;
status[str]; // 200

or more cleanly:

type StatusKey = keyof typeof status;
let str: StatusKey = "OK";
status[str]; // 200

// and to answer the question about reversal
status[status.OK as StatusKey]; // OK

See: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types

I had to access second level dynamic key from an object.

const myObj = {
"address": {
  "street": "Main ave",
  "city": "NYC"
}

}

secondLevelDynamic(field: string) {

let dynamicKey = field as keyof myObj;

let myProp = this.myObj['address'];
let myDynamicPropValue = myProp[dynamciKey]

}

Actually there's a simpler fix, you just have to just use const assertion.

Here's the fix:

const str = "OK" as const; // <-- add "as const", now `str` has type of "OK" instead of `string`
status[str]; // No more error!

Playground link.

I came across the same problem when I wanted to implement an express patch request on a resource. I wanted to parse through the request body and update only which are specified in the body.

Following is the demo code which I found to be working by using TypeScript Generics.

interface ObjType {
  name: string,
  addr: string
}

const demo: ObjType = {
  name: "Foo",
  addr: "Bar"
}

const keyList: Array<keyof ObjType> = ["name", "addr"]

const keyListFromObject: Array<keyof ObjType> = Object.keys({ name: "John", addr: "Alexandria" }) as Array<keyof ObjType>
keyList.forEach((el) => console.log(demo[el]))

keyListFromObject.forEach((el) => console.log(demo[el]))
Related