I'm fairly new to Typescript, and trying to figure out how to best handle this scenario that I keep running into.
When i have a function, and I pass a typed object into that function, I need to access properties of that object with a variable like obj[key]
. Trying to access a property like that throws an error like:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'ScrapedBuilding'. No index signature with a parameter of type 'string' was found on type
Makes sense, so I can add something like [key: string]: string | number
which satisfies that error, but now I can add any property to my object making the whole point of typing the object in the first place pointless.
How can I access properties of my object with a variable, but still have a strongly typed object?
A small example
function foo(data: DataDef) {
const key = some logic to determine what property
const prop = data[key] // Error unless I allow [key: type]: type
}
CodePudding user response:
Two ways.
type Data = {
first: number;
second: number;
third: number;
};
type DataProps = Data;
or, if the properties aren't required,
type DataProps = 'first'|'second'|'third';
type Data = Record<DataProps, number>;
Either way,
const data: Data = {
first: 1,
second: 2,
third: 3
};
function getProp(data: Data, key: DataProps) {
return data[key];
}
should work.
CodePudding user response:
Here is an example of what you are trying to do I think. In your example you had "some logic to determine what property" so I figured you need to loop through the object there? In this case I checked to see if the key was equal to the string "first" you could do any logic there and still use the key to get the property of data.
type Data = {
first: number;
second: number;
third: number;
};
const data = {
first: 1,
second: 2,
third: 3
};
const getFirst = (data: Data): number => {
for (const [key, value] of Object.entries(data)) {
console.log(`${key}: ${value}`);
if (key === "first") {
return data[key];
}
}
return -1;
};
Another good way to do what you are trying is to use an enum to index the object then because the enum is defined and not just any random string you wont run into the same problem. I prefer to use enums to have strongly typed objects.
enum DataEnum {
first = "first",
second = "second",
third = "third"
}
interface DataTypeEnum {
[DataEnum.first]: number;
[DataEnum.second]: number;
[DataEnum.third]: number;
}
const dataEnum = {
[DataEnum.first]: 1,
[DataEnum.second]: 2,
[DataEnum.third]: 3,
}
const getFirstEnum = (d: DataTypeEnum): number => {
for (const [key, value] of Object.entries(d)) {
console.log(`${key}: ${value}`);
if (key === DataEnum.first) {
return d[key];
}
}
return -1;
};