2

In javascript, accessing object value via obj.field can be done by obj['field'] too. But produces error in typescript.

function F() {
  const obj = { field: "firstname", title: "First Name", value: 10 };
  let field = "field"
  console.log(obj[field]); // <-- Typescript error: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ field: string; title: string; value: number; }'
}

How can I cast obj to resolve typescript error?

2
  • 1
    It complains because the property key is evaluated at runtime. You can cast the key to assure typescript compiler it will be always one of those valuesconsole.log(obj[field as keyof typeof obj] ) Commented Feb 12, 2024 at 7:40
  • @SvetoslavPetkov works well tnx Commented Feb 12, 2024 at 7:47

3 Answers 3

4

You can explicitly annotate field as follows:

function F() {
  const obj = { field: 'firstname', title: 'First Name', value: 10 };
  const field: keyof typeof obj = 'field'; // field is narrowed to be a known key of obj instead of 'string'

  console.log(obj[field]); // no type error here
}
Sign up to request clarification or add additional context in comments.

4 Comments

I think the term narrow is being used incorrectly here. That's actually just an explicit type annotation.
yes, in the technical typescript sense you are right. I thought of narrowing in a broader sense here. would you agree with 'explicitly narrow'?
Not really, I'd say narrowing is reducing a wider type to a more specific one with conditions in your code that are interpreted by the CFA.
edited answer to wording: 'explicitly annotate'
2

Your issue is that you're storing the field variable in a mutable variable (you're using let to assign). That means the type-engine can not assume that the type of field is Literal<"field">, but rather just string, of which obj doesn't have a typing.

To remedy the issue, either write const field = "field", or add a manual typing to it, to limit the field variable's possible features:

function F() {
  const obj = { field: "firstname", title: "First Name", value: 10 };
  let field = "field" as "field" | "title" | "value"
  console.log(obj[field]); // <-- No error
}

TS playground


If you don't want to re-write all the keys, you can do as @Hoopra says, and just use the keyof typeof obj syntax:

function F() {
  const obj = { field: "firstname", title: "First Name", value: 10 };
  let field = "field" as keyof typeof obj
  console.log(obj[field]); // <-- No error
}

TS playground

1 Comment

that's a snippet from a big set of different type of objects, so I can't write any literals. but your second recommendation works well. tnx
0

TypeScript is doing it job well and protecting you from accessing a property that may not exist anymore by the time of execution.

let field = "field"

TS is inferring the type to be string, and therefore you may be accessing something on obj that doesn't exist. The reason is because let declares a value, and someone could come and add a line to change the value of field. And so, the compiler has no assurance that the value remains "field".

One way to fix this, and understand why TS does that, is to change let field to be const field. A constant doesn't change and so TS takes this into account and will allow you do to this.

If you need field to be dynamic, you could also do something like the following;

let field: 'field' | 'title' = "field"

This way the compiler knows that obj[field] has the type string, since both keys refer to a string value.

2 Comments

field is used in a lot of object types and can almost be considered dynamic (but I am sure it is one of the allowed values). that's why I am using let and I can't write any literals.
I undestand that typescript does its job well, but problem is I have little time to deliver first version of product, and cannot refactor that field among so many different object types :(

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.