Issue
I want to have an object that can contain strings in its root, and an array of strings in the arrayValues object. This is a config object that will be used in an app.
This object and its keys will be dynamic (defined via user state values) so I have no way of specifying types based on the specific key name.
I will be adding arrays to "arrayValues" from one part of my app, and strings to "filters" from another part of the app.
However I’m getting errors when accessing object properties:
TS PLAYGROUND
export type FiltersStateType = {
filters:
| {
arrayValues: { // note: this should only appear once in filters object
[key: string]: Array<string>
}
}
| { [key: string]: string }
}
const example: FiltersStateType = {
filters: {
arrayValues: {
sandwiches: ['ham', 'cheese'],
milkshakes: ['choc', 'banana'],
},
randomString: 'hello',
},
}
example.filters.arrayValues.sandwiches.filter((item: string) => item !== 'ham')
// ❌ Property 'sandwiches' does not exist on type 'string | { [key: string]: string[]; }'.
// ❌ Property 'sandwiches' does not exist on type 'string'
const stringKey = 'milkshakes'
example.filters.arrayValues[stringKey].filter((item: string) => item !== 'choc')
// ❌ Element implicitly has an 'any' type because expression of type '"milkshakes"' can't be used to index type 'string | { [key: string]: string[]; }'.
// ❌ Property 'milkshakes' does not exist on type 'string | { [key: string]: string[]; }'
example.filters.randomString = 'bye'
// ❌ Property 'randomString' does not exist on type '{ arrays: { [key: string]: string[]; }; } | { [key: string]: string; }'.
// ❌ Property 'randomString' does not exist on type '{ arrays: { [key: string]: string[]; }; }
I expected TypeScript to understand the types found in these objects as I’ve specified them in FiltersStateType.
Do you know why this is happening?
Solution
This is happening because of the highlighted Union:
export type FiltersStateType = {
filters:
| {
arrayValues: {
[key: string]: Array<string>
}
}
| { [key: string]: string } // this one
// ---^^^^^^^^^^^^^^^^^^^^^^^^^---
}
As you have specified { [key: string]: string }
in the union, the below object stands valid:
const example: FiltersStateType = {
filters: {
arrayValues: 'hello',
},
}
TypeScript is unable to enforce the shape of the value of arrayValues
key, as { [key: string]: string }
itself means any key can have value of string. Ideally, you must change the structure of your type definitions something like this:
// this is just an example, you should
// restructure as per your requirements
type FiltersStateType = {
filters: { [key: string]: string },
arrayValues: { [key: string]: Array<string> }
}
Answered By – Vinay Sharma
Answer Checked By – Gilberto Lyons (BugsFixing Admin)