'string' can't be used to index type '{}'
items: object[],
While technically it is a JavaScript object, the type can be better. For Typescript to correctly help you identify mistakes when accessing objects properties, you need to tell it the exact shape of the object. If you type it as object
, typescript cannot help you with that. Instead you could tell it the exact properties and datatypes the object has:
let assistance: { safe: string } = { safe: 1 /* typescript can now tell this is wrong */ };
assistance.unknown; // typescript can tell this wont really work too
Now in the case that the object can contain any sort of key / value pair, you can at least tell typescript what type the values (and the keys) have, by using an object index type:
items: {
[key: string]: number | string,
}[]
That would be the accurate type in the case given.
Use Generics
// bad
const _getKeyValue = (key: string) => (obj: object) => obj[key];
// better
const _getKeyValue_ = (key: string) => (obj: Record<string, any>) => obj[key];
// best
const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T) =>
obj[key];
Bad - the reason for the error is the object
type is just an empty object by default. Therefore it isn't possible to use a string
type to index {}
.
Better - the reason the error disappears is because now we are telling the compiler the obj
argument will be a collection of string/value (string/any
) pairs. However, we are using the any
type, so we can do better.
Best - T
extends empty object. U
extends the keys of T
. Therefore U
will always exist on T
, therefore it can be used as a look up value.
Here is a full example:
I have switched the order of the generics (U extends keyof T
now comes before T extends object
) to highlight that order of generics is not important and you should select an order that makes the most sense for your function.
const getKeyValue = <U extends keyof T, T extends object>(key: U) => (obj: T) =>
obj[key];
interface User {
name: string;
age: number;
}
const user: User = {
name: "John Smith",
age: 20
};
const getUserName = getKeyValue<keyof User, User>("name")(user);
// => 'John Smith'
If it's a pedantic javascript object that doesn't make sense to create type definitions per field for, and doesn't make sense as a class definition, you can type the object with any
and typescript will let you index however you want.
ex.
//obviously no one with two brain cells is going to type each field individually
let complicatedObject: any = {
attr1: 0,
attr2: 0,
...
attr999: 0
}
Object.keys(complicatedObject).forEach(key => {
complicatedObject[key] += 1;
}