From 6f3373ce98333c68532366aaeec8ccdd8d413ca3 Mon Sep 17 00:00:00 2001 From: Taylor Lodge Date: Tue, 5 Mar 2024 11:58:44 +1300 Subject: [PATCH] fix(builders): _xstateTree property typed incorrectly Passes through the selector/action/slots types correctly when building a machine with createXStateTreeMachine so that accessing properties on the _xstateTree property has the correct types instead of the defaults --- src/builders.spec.tsx | 49 ++++++++++++++++++++++++++++++++++++++++++- src/builders.tsx | 11 +++++++--- src/types.ts | 28 +++++++++++++++++++++---- xstate-tree.api.md | 12 +++++------ 4 files changed, 86 insertions(+), 14 deletions(-) diff --git a/src/builders.spec.tsx b/src/builders.spec.tsx index 4a1ed14..de7249d 100644 --- a/src/builders.spec.tsx +++ b/src/builders.spec.tsx @@ -1,13 +1,60 @@ import { act, render, waitFor } from "@testing-library/react"; import { createMemoryHistory } from "history"; import React from "react"; +import { setup } from "xstate"; -import { buildRoutingMachine, viewToMachine } from "./builders"; +import { + buildRoutingMachine, + createXStateTreeMachine, + viewToMachine, +} from "./builders"; import { buildCreateRoute } from "./routing"; import { XstateTreeHistory } from "./types"; import { buildRootComponent } from "./xstateTree"; describe("xstate-tree builders", () => { + describe("createXStateTreeMachine", () => { + it("passes through the selectors/actions/slots/view types correctly through to the _xstateTree property", () => { + const machine = setup({ + types: { + context: {} as { foo: string; bar: string }, + }, + }).createMachine({ + context: { + foo: "foo", + bar: "bar", + }, + initial: "idle", + states: { + idle: {}, + }, + }); + + const xstateTreeMachine = createXStateTreeMachine(machine, { + selectors({ ctx }) { + return { + foobar: ctx.foo + ctx.bar, + }; + }, + View({ selectors }) { + return
{selectors.foobar}
; + }, + }); + + const View = xstateTreeMachine._xstateTree.View; + + ; + }); + }); + describe("viewToMachine", () => { it("takes a React view and wraps it in an xstate-tree machine that renders that view", async () => { const ViewMachine = viewToMachine(() =>
hello world
); diff --git a/src/builders.tsx b/src/builders.tsx index 68a828b..2219e02 100644 --- a/src/builders.tsx +++ b/src/builders.tsx @@ -2,9 +2,9 @@ import React from "react"; import { setup, type AnyStateMachine, - type ContextFrom, AnyStateNodeConfig, createMachine, + type ContextFrom, } from "xstate"; import { AnyRoute } from "./routing"; @@ -37,11 +37,16 @@ export function createXStateTreeMachine< >( machine: TMachine, options: V2BuilderMeta -): XstateTreeMachine { +): XstateTreeMachine { const selectors = options.selectors ?? (({ ctx }) => ctx); const actions = options.actions ?? (() => ({})); - const machineWithMeta = machine as unknown as XstateTreeMachine; + const machineWithMeta = machine as unknown as XstateTreeMachine< + TMachine, + TSelectorsOutput, + TActionsOutput, + TSlots + >; machineWithMeta._xstateTree = { selectors: selectors as any, actions: actions as any, diff --git a/src/types.ts b/src/types.ts index 2129750..0276f0b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -88,15 +88,35 @@ export type MatchesFrom = ( /** * @internal */ -export type XstateTreeMachineInjection = { - _xstateTree: XstateTreeMachineStateSchemaV2; +export type XstateTreeMachineInjection< + TMachine extends AnyStateMachine, + TSelectorsOutput = ContextFrom, + TActionsOutput = Record, + TSlots extends readonly Slot[] = Slot[] +> = { + _xstateTree: XstateTreeMachineStateSchemaV2< + TMachine, + TSelectorsOutput, + TActionsOutput, + TSlots + >; }; /** * @public */ -export type XstateTreeMachine = TMachine & - XstateTreeMachineInjection; +export type XstateTreeMachine< + TMachine extends AnyStateMachine, + TSelectorsOutput = ContextFrom, + TActionsOutput = Record, + TSlots extends readonly Slot[] = Slot[] +> = TMachine & + XstateTreeMachineInjection< + TMachine, + TSelectorsOutput, + TActionsOutput, + TSlots + >; /** * @public diff --git a/xstate-tree.api.md b/xstate-tree.api.md index e081024..6be27dd 100644 --- a/xstate-tree.api.md +++ b/xstate-tree.api.md @@ -115,7 +115,7 @@ export function buildRoutingMachine(_routes: TRoutes export type CanHandleEvent = (e: EventFrom) => boolean; // @public -export function createXStateTreeMachine, TActionsOutput = Record, TSlots extends readonly Slot[] = []>(machine: TMachine, options: V2BuilderMeta): XstateTreeMachine; +export function createXStateTreeMachine, TActionsOutput = Record, TSlots extends readonly Slot[] = []>(machine: TMachine, options: V2BuilderMeta): XstateTreeMachine; // @public export const genericSlotsTestingDummy: any; @@ -383,13 +383,13 @@ export type XstateTreeHistory = History_2<{ // Warning: (ae-incompatible-release-tags) The symbol "XstateTreeMachine" is marked as @public, but its signature references "XstateTreeMachineInjection" which is marked as @internal // // @public (undocumented) -export type XstateTreeMachine = TMachine & XstateTreeMachineInjection; +export type XstateTreeMachine, TActionsOutput = Record, TSlots extends readonly Slot[] = Slot[]> = TMachine & XstateTreeMachineInjection; // Warning: (ae-internal-missing-underscore) The name "XstateTreeMachineInjection" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) -export type XstateTreeMachineInjection = { - _xstateTree: XstateTreeMachineStateSchemaV2; +export type XstateTreeMachineInjection, TActionsOutput = Record, TSlots extends readonly Slot[] = Slot[]> = { + _xstateTree: XstateTreeMachineStateSchemaV2; }; // @public (undocumented) @@ -400,8 +400,8 @@ export type XstateTreeMachineStateSchemaV2