I am building a react-native / typescript project and I run into this problem:
I have a list of objects that I fetch from an API, and it looks something like this:
[{ qwe1: 'Bob', qwe2: 18 },
{ qwe1: 'James', qwe2: 19 }]
But I don't want to have those keys names for this object (qwe1, qwe2), and I also don't want to run throw all the objects and change all the keys.
So I thought to make an object that will hold all the keys of the original object but give nice access for better names for them, it's look something like that :
{
name: 'qwe1',
age: 'qwe2'
}
and then when I want to access the user's name for example I will do this:
myObject[objectKeyNames.name]
I also made a type for the object that look like this:
{qwe1: string; qwe2: number;}
But then an error was showed up saing that:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '<the name of the type>'
I am really new to typescript and I don't really get this, I would be most grateful for your support.
Thanks in advance :)
CodePudding user response:
Define your objectKeyNames
object to specifically reference the keys from the data object.
For example
interface ApiResult {
qwe1: string;
qwe2: number;
}
const objectKeyNames: {[key: string]: keyof ApiResult} = {
name: "qwe1",
age: "qwe2"
};
// ---
const myObject: ApiResult = {
qwe1: "Bob",
qwe2: 18
};
console.log(myObject[objectKeyNames.name]);
Now Typescript knows that any key in objectKeyNames
will reference a valid property name from the API result.
As an aside, it's usually trivial to re-map one data format to another
const niceObjects = results.map(({ qwe1, qwe2 }) => ({
name: qwe1,
age: qwe2,
}));
CodePudding user response:
The whole point of TypeScript, as opposed to JavaScript, is type safety. In ordinary JavaScript, person.lastMane
will silently give you undefined
; TypeScript compiler will give you a compile-time warning that people don't have last manes. However, when you do person[attribute]
, where attribute
is a string whose value cannot be known at compile-time, TypeScript must wash hands of it. It can't know if attribute
will be "lastName"
, or if it will end up as "lastMane"
. So it goes "I don't know about this, this is on you."
In order to satisfy TypeScript, one thing you can do is have a lookup of accessors instead of a lookup of attribute names, so that you never need to dynamically look up an attribute by a string. Something like this:
interface APIPerson {
qwe1: string;
qwe2: number;
}
const personAccessors = {
"name": (apiPerson: APIPerson) => apiPerson.qwe1,
"age": (apiPerson: APIPerson) => apiPerson.qwe2,
};
const apiPerson: APIPerson = {
qwe1: "John",
qwe2: 25,
}
const personName = personAccessors.name(apiPerson);
console.log(personName);
EDIT: ...but Phil's solution is smarter :) I leave this up primarily because of the explanation of why you are getting the error.