Description
@xstate/react: typescript: state is always never after calling .matches() twice (in the same expression?)
function App () {
const [state, _] = useMachine(toggleMachine)
if (state.matches('a') || state.matches('b')) {
// ^ is of type `never`
console.log('do a thing')
}
/*
* doesn't work like this either
*
* if (state.matches('a')) {
* // ...
* } else if (state.matches('b')) {
* // ...
* }
*/
}
Is this an antipattern or am I doing something wrong?
Sadly I don't have enough typescript experience at the moment to understand the following code in detail:
https://github.com/davidkpiano/xstate/blob/d6eda85b28759b4cc296ca8f88633f2342045e31/packages/core/src/State.ts#L284-L291
Expected Result
I should be able to check if a state is either one of a state
Actual Result
I can't check if a type is one of either states
Reproduction
Simple Reproduction: https://codesandbox.io/s/xenodochial-faraday-05nmv
Additional context
{
"@xstate/react": "^0.8.1",
"xstate": "^4.7.6"
}
Workaround: state.matches<any>(...)
Workaround:
state.matches<any>(...)
Just wanted to call out that this work around doesn't solve the TypeScript error for me in the aforelinked Code Sandbox.
Regardless, thank you for this great library and constant support.
P.S. sign up as a Brave Content Creator to be able to receive BAT tips 馃挭
I wrote myself this little helper to work around this issue:
export function matchesAny(state: State<Context, Event, Schema>, ...values: string[]): boolean {
return values.reduce<boolean>((a, s) => a || state.matches(s), false);
}
Hm, actually it could be simplified to this probably:
export function matchesAny(state: State<Context, Event, Schema>, ...values: string[]): boolean {
return values.some(s => state.matches(s));
}
Make sure you're using the latest version of TypeScript as well. For anyone who is still seeing issues, what version of TS do you have?
Here is a codesandbox with a simple reproduction. Interestingly I seem to sometimes only get an IDE error and sometimes an error when Typescript is compiling as well.
Typescript 3.8.2
xstate 4.8.1
@xstate/react 0.8.1
export function matchesAny(state: State
, ...values: string[]): boolean {
return values.some(s => state.matches(s));
}
Where do I import Context and Schema from?
@cschlittPHI Those are types you provide yourself.
I think you should be able to set unknown for the type parameters, as they are not depended on by the implementation.
Still an issue for me with latest everything: typescript 3.8.3, xstate 4.9.1, @xstate/react 0.8.1... in my case tsc is happy for some reason, it's only tsserver complaining. The <any> (or <unknown>) workaround doesn't help unfortunately.
EDIT: Found a working workaround,
const aState = state.matches("a");
if (aState) { return <A />; }
const bState = state.matches("b");
if (bState) { return <B />; }
... works when this doesn't:
if (state.matches("a")) { return <A />; }
if (state.matches("b")) { return <B />; }
I didn't realize this issue has been raised before. I experienced the same problem and reported it here https://github.com/davidkpiano/xstate/issues/1142.
@jscheid solution of moving state.matches into the variable works.
Another workaround is to reassign the state.
import { State } from 'xstate';
import { useMachine } from '@xstate/react';
const Comp = () => {
let [state, send] = useMachine(myMachine);
if (state.matches('maybeNo'){
return;
}
state = state as State<MyContext, MyEvent>
// continue to use state as it no longer has the "never" type
}
The solution I've came up to solve this problem is simply assign the initial state value to different constants and then use those constants
const [state, send] = useMachine(machine)
const states = {
submited: state,
submitting: state,
idle: state,
invalid: state,
}
if (states.submited.matches('submited')) {/* ... */}
if (state.idle.matches('idle') {/* ... */}
Most helpful comment
Still an issue for me with latest everything: typescript 3.8.3, xstate 4.9.1, @xstate/react 0.8.1... in my case
tscis happy for some reason, it's onlytsservercomplaining. The<any>(or<unknown>) workaround doesn't help unfortunately.EDIT: Found a working workaround,
... works when this doesn't: