I have a simple MobX Store:
import { observable, action, makeObservable } from "mobx"
import { BoxShadow, ShadowValues } from "./types"
import { boxShadow } from "./constants"
interface IStore {
shadow: BoxShadow
}
export class Store implements IStore {
shadow: BoxShadow = {
selected: "DEFAULT",
list: boxShadow,
}
constructor() {
makeObservable(this, {
shadow: observable,
updateShadow: action.bound,
})
}
updateShadow(selected: ShadowValues) {
this.shadow.selected = selected
}
}
export const store = new Store()
The BoxShadow
& ShadowValues
type consists of:
type Point = {
x: number
y: number
}
export type ShadowValues =
| "none"
| "sm"
| "DEFAULT"
| "md"
| "lg"
| "xl"
| "2xl"
| "3xl"
export type Shadow = {
offset: Point
blur: number
shadow: number
color: string
}
export type ShadowList = Record<ShadowValues, Shadow[] | Shadow>
export type BoxShadow = {
selected: ShadowValues
list: ShadowList
}
I am using HTML select & updating the boxShadow
values:
import * as React from "react"
import { toJS } from "mobx"
import { observer } from "mobx-react"
import { useStore } from "./context"
import { Select } from "./Select"
import { shadows } from "./constants"
import { ShadowValues } from "./types"
export default observer(function App() {
const { shadow, updateShadow } = useStore()
console.log(toJS(shadow.list[shadow.selected])[0]["offset"])
console.log(shadow.list[shadow.selected].offset)
return (
<div className="w-full flex flex-col items-center">
<h1 className="text-4xl">Access MobX Proxy</h1>
<Select
className="flex-1 w-56"
label="Box Shadow"
checkmark={true}
options={shadows}
selectedOption={
shadows.filter(({ value }) => value === shadow.selected)[0]
}
onChange={(selectedOption) => {
const selected = selectedOption.value as ShadowValues
updateShadow(selected)
}}
/>
</div>
)
})
However, I'm unable to access selectedShadow.color
or selectedShadow.offset
without TypeScript yelling at me.
The boxShadow
values consists of an object or an array of objects like:
export const boxShadow = {
none: {
offset: { x: 0, y: 0 },
blur: 0,
shadow: 0,
color: "rgba(0, 0, 0, 0)",
},
sm: {
offset: { x: 0, y: 1 },
blur: 2,
shadow: 0,
color: "rgba(0, 0, 0, 0.05)",
},
DEFAULT: [
{
offset: { x: 0, y: 1 },
blur: 3,
shadow: 0,
color: "rgba(0, 0, 0, 0.1)",
},
{
offset: { x: 0, y: 1 },
blur: 2,
shadow: 0,
color: "rgba(0, 0, 0, 0.06)",
},
],
md: [
{
offset: { x: 0, y: 4 },
blur: 6,
shadow: -1,
color: "rgba(0, 0, 0, 0.1)",
},
{
offset: { x: 0, y: 2 },
blur: 4,
shadow: -1,
color: "rgba(0, 0, 0, 0.06)",
},
],
lg: [
{
offset: { x: 0, y: 10 },
blur: 15,
shadow: -3,
color: "rgba(0, 0, 0, 0.1)",
},
{
offset: { x: 0, y: 4 },
blur: 6,
shadow: -2,
color: "rgba(0, 0, 0, 0.05)",
},
],
xl: [
{
offset: { x: 0, y: 20 },
blur: 25,
shadow: -5,
color: "rgba(0, 0, 0, 0.1)",
},
{
offset: { x: 0, y: 10 },
blur: 10,
shadow: -5,
color: "rgba(0, 0, 0, 0.04)",
},
],
"2xl": {
offset: { x: 0, y: 25 },
blur: 50,
shadow: -12,
color: "rgba(0, 0, 0, 0.25)",
},
"3xl": {
offset: { x: 0, y: 35 },
blur: 60,
shadow: -15,
color: "rgba(0, 0, 0, 0.3)",
},
}
I am trying to access it using toJS
but still can't access nested properties. The .toJS
method however displays perfectly fine if I console.log
it:
import * as React from "react"
import { toJS } from "mobx"
import { observer } from "mobx-react"
import { useStore } from "./context"
import { Select } from "./Select"
import { shadows } from "./constants"
import { ShadowValues } from "./types"
export default observer(function App() {
const { shadow, updateShadow } = useStore()
console.log(toJS(shadow.list[shadow.selected])[0]["offset"])
console.log(shadow.list[shadow.selected].offset)
return (
<div className="w-full flex flex-col items-center">
<h1 className="text-4xl">Access MobX Proxy</h1>
<Select
className="flex-1 w-56"
label="Box Shadow"
checkmark={true}
options={shadows}
selectedOption={
shadows.filter(({ value }) => value === shadow.selected)[0]
}
onChange={(selectedOption) => {
const selected = selectedOption.value as ShadowValues
updateShadow(selected)
}}
/>
</div>
)
})
Check out the console.log
above. I want to access those values somehow.
If I do shadow.list[shadow.selected][0].color
, I get:
TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'string | Shadow[] | Shadow'. Property '0' does not exist on type 'string | Shadow[] | Shadow'.
And if I do shadow.list[shadow.selected].color
, I get:
TS2339: Property 'color' does not exist on type 'string | Shadow[] | Shadow'. Property 'color' does not exist on type 'string'.
Which is correct but I don't know how to solve it :(
I have made a minimal Codesandbox to see the demo. Note that it sometimes console.log
's on Codesandbox but doesn't locally but it does give TS errors on Codesandbox too.
How can I solve the issue? I want to access the object value sm.offset
plus the array of objects too DEFAULT[0].offset
& DEFAULT[1].offset
:)
As I have the type as Shadow | Shadow[]
, I had to add a guard to narrow the type when I had to access it. So I did the following to make it work:
if (Array.isArray(selected)) {
console.log(selected[0].offset);
} else {
console.log(selected.offset);
}
I tried typeof selected
& it was giving me object
all the time & I forgot that JS considers everything an object
so never tried the above stuff :)