Skip to content
TypeScriptJavaScript Object

TypeScript Errors with Dynamic Properties

This warning has been showing up in the Vercel build logs for my Habit Tracker app I’ve been working on:

1:38 Warning: Unexpected any. Specify a different type. @typescript-eslint/no-explicit-any

The refers to the helper function that computes the totals of each habit to be displayed in the table footer.

export function returnTotal(records: any[] | undefined, habit: string) { return records?.reduce((accumulator, currentValue) => { return accumulator + currentValue[habit]; }, 0); }

I have an existing type for records called RecordType

export interface RecordType { date: string; dateAsNumber: number; jacks: number; meditation: number; pullups: number; pushups: number; situps: number; stairs: number; }

So I can just type that parameter like this

records: RecordType[]

As soon as I do that though, I get

TS7053: Element implicitly has an any type because expression of type string can't be used to index type RecordType No index signature with a parameter of type string was found on type RecordType

which refers to the currentValue[habit] expression in the return statement.

This took a little while to figure out, but eventually I learned (or possibly relearned) that you have to do something special in TypeScript when dynamically accessing Object properties. TypeScript doesn’t just let you use a parameter with type string to index your Object. You need to come up with something that has the keyof type.

With the help of this [StackOverflow question](https://stackoverflow. com/questions/62438346/how-to-dynamically-access-object-property-in-typescript), I refactored to:

export function returnTotal(records: RecordType[] | undefined, habit: string) { return records?.reduce((accumulator, currentValue) => { return accumulator + currentValue[habit as keyof typeof currentValue]; }, 0); }

Which yields one more TS error:

TS2365: Operator + cannot be applied to types number and string | number

pointing to accumulator + currentValue[habit as keyof typeof currentValue] in the return. This is because the RecordType has one property of type string.

Refactoring that line to

accumulator + (currentValue[habit as keyof typeof currentValue] as number)

clears the final TS error and the build warning is gone.

Final working function:

export function returnTotal(records: RecordType[], habit: string) { return records?.reduce((accumulator, currentValue) => { return accumulator + (currentValue[habit as keyof typeof currentValue] as number); }, 0); }

This was a great little exercise in dealing with Object and TypeScript errors.

Update:

Shortly after publishing this, Justin from Virtual Coffee read this and provided a refactor to help drop the usage of the as keyword, which is problematic. I really like how CountableHabits strips out the properties that I don’t want to count and TypeScript can’t count. It makes for a concise final solution. Thanks Justin!

type CountableHabits = Omit<RecordType, "date" | "dateAsNumber">; export function returnTotal(records: RecordType[], habit: keyof CountableHabits) { return records?.reduce((accumulator, currentValue) => { return accumulator + currentValue[habit]; }, 0); }