2

In plain JavaScript, we can read the value of an object's property using a variable.

That is, this is valid:

let obj = { a: 100, b: 'Need help with TypeScript', c: new Date() };
let prop = 'b';
console.log( obj[prop] );  // Need help with TypeScript

However, the below TypeScript annotation declaring prop as a string results in the indicated index error.

let obj: object = { a: 100, b: 'Need help with TypeScript', c: new Date() };
let prop: string = 'b';
console.log( obj[prop] );  // TypeScript error element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.

How should I be annotating the above?

Edit Dec. 9, 2019: When I posted the question, I had assumed there would be a generic answer and not an answer explicitly dependent upon my example object.

My use case is a function, that accepts as an argument a property's name. The function sorts an array based on the name of the property passed. The original JavaScript code that I'm porting to TS will sort any array of objects.

4
  • Does this answer your question? Typescript Error: type 'string' can't be used to index type X Commented Dec 6, 2019 at 20:13
  • Don't annotate obj at all; let it be inferred as type {a: number, b: number, c: number}. As for prop, you also don't need to annotate it, but you should either change let to const, or use let prop = "b" as const, like this. Commented Dec 6, 2019 at 20:15
  • @jcalz - thank you for your comment and example. I looked at the example and it works, but I don't understand why it works. Why does declaring the property as a a const make a difference. I've not yet had a chance to try your suggestion for my specific case. I will though. (Please see my edits made today.) Commented Dec 9, 2019 at 15:04
  • "Why does declaring the property as a const make a difference" The compiler treats const as signaling the intent that the value will not change (this is only true for primitives, but whatever), so it infers it as a narrower type. If you write let prop = "b", it guesses that you want prop to be a string because you might change it to some other string later. If you write const prop = "b", it knows that prop will always be "b" so it infers the string literal type "b". I'd be happy to turn this into an answer if it meets your needs. Commented Dec 10, 2019 at 14:39

1 Answer 1

1

Avoid object as a type, TS cannot deduct from this what to do.

Consider adding a type, that allows what you want to do:

let obj: { [key: string]: number } = { a: 100, b: 200, c: 300 };
let prop: string = 'b';
console.log( obj[prop] );

You can even refine this more by using the [key in type] syntax, where type is a type definition:

type props = 'a' | 'b' | 'c';
let obj: { [key in props]: number } = { a: 100, b: 200, c: 300 };
let prop: props = 'b';
console.log(obj[prop]);

Later the access to the named property could be simplified by using an interface. Using this as a basis, the keyof operator is very helpful:

interface IProps{
  a: number,
  b: number,
  c: number
}
let obj: { [key in keyof IProps]: number} = { a: 100, b: 200, c: 300 };
let prop: keyof IProps = 'b';
console.log(obj[prop]);

The last is more for demonstration, reference and showcase, not exactly the perfect answer the OP was looking for.

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

1 Comment

I guess accepting an answer late is better than never accepting it. :-) (I did not end up adopting TS.)

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.