Reputation: 111
I need to access the properties of my object dynamically using a given string as the key.
I have checked the following, but I can't find the answer I am looking for:
TypeScript dynamic object properties
Typescript dynamic access property
The error happens here:
state.value[container][id] // TS2571: Object is of type 'unknown'
When calling method 'getEntity' should I be passing an ENUM for the property instead of a string?
Below is a simplified example of my code - errors are on lines 57 and 69:
// interfaces for entity types - including these in this example seems unnecessary
import {
TaskFinances,
Plan,
Boundary,
BoundaryMapList
} from 'DTOs'
// for pulling data from REST
import axios, { AxiosResponse } from 'axios'
// VueJS composition AIP components
import { ref } from '@vue/composition-api'
// I want to imply that the State interface below is made up of string:unknown pairs
// so that I can reference it using a string key in the <getEntity> method below
interface Stately {
[key: string]: unknown
}
interface State extends Stately {
plans: { [key: number]: Plan };
taskFinances: { [key: number]: TaskFinances };
boundaryPointsByField: Boundary[];
}
const props: State = {
plans: {},
taskFinances: {},
boundaryPointsByField: []
}
const state = ref(props)
// I reference the properties of state using state.value.xxx because it is using the Vue3 Composition API Ref syntax
// Reduced to 2 lines following D.R.Y. principles
function getTaskFinances(activityId: number): Promise<TaskFinances> {
const taskFinances = getEntity(activityId, 'taskFinance', 'taskFinances') as unknown as Promise<TaskFinances>
return taskFinances
}
// Reduced to 2 lines following D.R.Y. principles
function getBoundaryPointsByField(fieldId: number): Promise<BoundaryMapList> {
const boundaryPoints = getEntity(fieldId, 'boundaryPointsByField', 'boundaryPointsByField') as unknown as Promise<BoundaryMapList>
return boundaryPoints
}
// Reduced to 2 lines following D.R.Y. principles
function getPlan(planId: number): Promise<Plan> {
const plan = getEntity(planId, 'plan', 'plans') as unknown as Promise<Plan>
return plan
}
// generic method to return requested entities and add them to my state object
async function getEntity(id: number, api: string, container: string): Promise<unknown> {
try {
const entity: unknown = state.value[container][id] // THROWS typescript error -TS2571: Object is of type 'unknown'-
if (entity) {
return entity
}
} catch (err) {
console.log(container, id, err)
}
const url = `${api}${id}`
try {
const { data }: AxiosResponse<unknown> = await axios.get(url)
console.log(api + ':', data)
state.value[container][id] = data // THROWS typescript error -TS2571: Object is of type 'unknown'-
return data
} catch (err) {
console.error(err)
}
}
// interface for my exported uiState object - I use this interface elsewhere in the app so I am exporting it
export interface IUIState {
getBoundaryPointsByField: (fieldId: number) => Promise<BoundaryMapList>;
getTaskFinances: (activityId: number) => Promise<TaskFinances>;
getPlan: (planId: number) => Promise<Plan>;
}
// export my uiState object
export const uiState: IUIState = {
getBoundaryPointsByField,
getTaskFinances,
getPlan
}
Upvotes: 0
Views: 2206
Reputation: 1386
This behaviour is intended.
Where you access your state here
const entity: unknown = state.value[container][id]
state.value[container]
is now of type unknown
. You cannot try and access [id]
behind it because TS has no clue if that even exists on state.value[container]
. It's unknown. That is the downfall of using unknown. If you know your API responses, create interfaces for them and declare those as possible types for your state.
Otherwise, if you want to force it to work without types, you'd have to use any
instead of unknown.
You can see this behaviour reproduced here. You can see how the variable b
is of type unknown and throws an error already. If you change the type to any
in the interface, it will work but you won't get any type-safety.
Upvotes: 1