S
SolidJS•2mo ago
cambiata

Support for discriminated union types?

Being new to SolidJS, I'm surprised to find that the type checking for discriminated union types in signals or stores seems to be limited. See example below. From my perspective this is very problematic. Am I missing something? Are there any decent workarounds? Plans for support? In the following example, the type checker complains about the the missing state().value field, in spite of the state().type === 'Data' being matched in the Switch statement:
type UnionType = { type: 'Empty' } | { type: 'Data', value: number };

const App: Component = () => {
const [state, setState] = createSignal<UnionType>({ type: 'Empty' });
return <>
<p>{JSON.stringify(state())}</p>
<button onClick={_ => setState({ type: 'Data', value: 123 })}>Set</button>

<Switch fallback={<div>Fallback</div>}>
<Match when={state().type = 'Empty'}>
<div>Type is 'Empty'</div>
</Match>
<Match when={state().type === 'Data'}>
<div>Type is 'Data': data={state().value}</div> // <-- 'value' does not exist...
</Match>
</Switch>
</>
};
type UnionType = { type: 'Empty' } | { type: 'Data', value: number };

const App: Component = () => {
const [state, setState] = createSignal<UnionType>({ type: 'Empty' });
return <>
<p>{JSON.stringify(state())}</p>
<button onClick={_ => setState({ type: 'Data', value: 123 })}>Set</button>

<Switch fallback={<div>Fallback</div>}>
<Match when={state().type = 'Empty'}>
<div>Type is 'Empty'</div>
</Match>
<Match when={state().type === 'Data'}>
<div>Type is 'Data': data={state().value}</div> // <-- 'value' does not exist...
</Match>
</Switch>
</>
};
The error output:
Property 'value' does not exist on type 'UnionType'.
Property 'value' does not exist on type '{ type: "Empty"; }'.ts(2339)
Property 'value' does not exist on type 'UnionType'.
Property 'value' does not exist on type '{ type: "Empty"; }'.ts(2339)
7 Replies
bigmistqke 🌈
bigmistqke 🌈•2mo ago
the problem is rather that jsx on itself will not narrow types. https://docs.solidjs.com/reference/components/show has a callback form that can help with type-narrowing
<Show when={signal()}>
{
// signal will be Accessor<NonNullable<T>>
(signal) => ...
}
</Show>
<Show when={signal()}>
{
// signal will be Accessor<NonNullable<T>>
(signal) => ...
}
</Show>
you can also use the keyed version
<Show when={signal()} keyed>
{
// signal will be NonNullable<T>
(signal) => ...
}
</Show>
<Show when={signal()} keyed>
{
// signal will be NonNullable<T>
(signal) => ...
}
</Show>
https://docs.solidjs.com/reference/components/switch-and-match#lessswitchgreater-lessmatchgreater <Match/> also seems to support a callback as a child, but I am less familiar with <Match/>
REEEEE
REEEEE•2mo ago
Do note that I'm pretty sure keyed causes rerenders from what I remember. Match has the same options as Show, keyed and non-keyed callbacks for children
cambiata
cambiata•2mo ago
Thanks guys! Found what seems to be a decent solution in this thread: https://discord.com/channels/722131463138705510/1074415945986080808/1074415948137758750 Here's an example:
type UnionType = { type: 'Empty' } | { type: 'Data', value: number };

const App: Component = () => {
const [state, setState] = createStore<UnionType>({ type: 'Empty' });
return <>
<button onClick={_ => setState({ type: 'Data', value: Math.random() })}>Set Data</button>

<Switch>
<Match when={state.type === 'Empty'}>
<h3>Empty</h3>
</Match>
<Match when={state.type === 'Data' && state}>
{state => <h3>Data value: {state().value}</h3>}
</Match>
</Switch>
</>
};
type UnionType = { type: 'Empty' } | { type: 'Data', value: number };

const App: Component = () => {
const [state, setState] = createStore<UnionType>({ type: 'Empty' });
return <>
<button onClick={_ => setState({ type: 'Data', value: Math.random() })}>Set Data</button>

<Switch>
<Match when={state.type === 'Empty'}>
<h3>Empty</h3>
</Match>
<Match when={state.type === 'Data' && state}>
{state => <h3>Data value: {state().value}</h3>}
</Match>
</Switch>
</>
};
No keyed involved, so perhaps no performance penalties.
bigmistqke 🌈
bigmistqke 🌈•2mo ago
Yes the callback form should be good!
cambiata
cambiata•2mo ago
If anyone's interested, here's a gist that shows how to deal with discriminated unions both in the view rendering and in the store:
cambiata
cambiata•2mo ago
Gist
Displays a way to deal with discriminated union types in Solid-js, ...
Displays a way to deal with discriminated union types in Solid-js, jsx view as well as store - Discriminated unions in Solid.js.tsx