Skip to content

Commit

Permalink
fix(core): fix type ExtractStateValue to describe real state value
Browse files Browse the repository at this point in the history
Consider the following schema:

    type LightStateSchema = {
      states: {
        green: {},
        yellow: {},
        red: {
          states: {
            walk: {},
            wait: {},
            stop: {},
          },
        },
      },
    }

The current implementation of the ExtractStateValue type generates the
following type:

    {
      green?: {} | undefined
      yellow?: {} | undefined
      red?: "walk" | "wait" | "stop" | {
        walk?: {} | undefined
        wait?: {} | undefined
        stop?: {} | undefined
      } | undefined
    }

This does not describe the StateValue of LightStateSchema as provided
in State.value.

The new implementation generates the following type:

    "green" | "yellow" | "red" | {
      red?: "walk" | "wait" | "stop" | undefined
    }
  • Loading branch information
jirutka committed Aug 25, 2020
1 parent 87a12c1 commit f51614d
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/ten-seahorses-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': minor
---

Fix type `ExtractStateValue` so that it generates a type actually describing a `State.value`
27 changes: 18 additions & 9 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,25 @@ export interface StateValueMap {
*/
export type StateValue = string | StateValueMap;

type KeysWithStates<
TStates extends Record<string, StateSchema> | undefined
> = TStates extends object
? {
[K in keyof TStates]-?: TStates[K] extends { states: object } ? K : never;
}[keyof TStates]
: never;

export type ExtractStateValue<
TS extends StateSchema<any>,
TSS = TS['states']
> = TSS extends undefined
? never
: {
[K in keyof TSS]?:
| (TSS[K] extends { states: any } ? keyof TSS[K]['states'] : never)
| ExtractStateValue<TSS[K]>;
};
TSchema extends Required<Pick<StateSchema<any>, 'states'>>
> =
| keyof TSchema['states']
| (KeysWithStates<TSchema['states']> extends never
? never
: {
[K in KeysWithStates<TSchema['states']>]?: ExtractStateValue<
TSchema['states'][K]
>;
});

export interface HistoryValue {
states: Record<string, HistoryValue | undefined>;
Expand Down

0 comments on commit f51614d

Please sign in to comment.