From b854fec9e52dd6ada71212d52afdda805bd183f5 Mon Sep 17 00:00:00 2001 From: Daniel McCrystal Date: Sat, 6 Sep 2025 17:30:47 -0400 Subject: [PATCH] [wip] extractProperty proper typing with union types --- pkg/client/src/components/Table.tsx | 23 +++++------------------ pkg/client/src/fn.ts | 6 ++---- pkg/server/src/table.ts | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/pkg/client/src/components/Table.tsx b/pkg/client/src/components/Table.tsx index f68ee04..e2e23eb 100644 --- a/pkg/client/src/components/Table.tsx +++ b/pkg/client/src/components/Table.tsx @@ -41,33 +41,23 @@ export default (props: { tableKey: string }) => { ws.on("open", () => res(ws)); ws.on("error", () => res(ws)); }); - const sendWs = (msg: TWsIn) => wsPromise.then((ws) => ws.send(msg)); const wsEvents = fromPromise(wsPromise).flatMap((ws) => fromWebsocket(ws) ); onCleanup(() => wsPromise.then((ws) => ws.close())); - const presenceEvents = wsEvents.filter( - (evt) => evt.playersPresent !== undefined - ); - const gameConfig = extractProperty(wsEvents, "gameConfig").thru( createObservable ); + const view = extractProperty(wsEvents, "view").thru(createObservable); - const gameEvents = wsEvents.filter((evt) => evt.view !== undefined); - const resultEvents = wsEvents.filter((evt) => evt.results !== undefined); - - const players = createObservableWithInit( - presenceEvents.map((evt) => evt.playersPresent!), - [] + const players = extractProperty(wsEvents, "playersPresent").thru( + createObservable ); + const playerNames = createObservableStore( - wsEvents - .filter((evt) => evt.playerNames != null) - .map(({ playerNames }) => playerNames!) - .toProperty(), + extractProperty(wsEvents, "playerNames"), {} ); @@ -83,9 +73,6 @@ export default (props: { tableKey: string }) => { createEffect(() => sendWs({ ready: ready() })); createEffect(() => sendWs({ name: name() })); - const viewProp = gameEvents.map((evt) => evt.view).toProperty(); - - const view = createObservable(viewProp); return ( ( return store; }; -export const extractProperty = < - P extends string, - T extends { [key in P]?: any } ->( +type UnionKeys = T extends any ? keyof T : never; +export const extractProperty = >( obs: Observable, property: P ): Property => diff --git a/pkg/server/src/table.ts b/pkg/server/src/table.ts index f1835ee..37248d3 100644 --- a/pkg/server/src/table.ts +++ b/pkg/server/src/table.ts @@ -13,16 +13,16 @@ import { combine, constant, merge, Observable, pool, Property } from "kefir"; import Bus, { type Bus as TBus } from "kefir-bus"; import { log } from "./logging"; -export const WsOut = t.Object({ - playersPresent: t.Optional(t.Array(t.String())), - playerNames: t.Optional(t.Record(t.String(), t.String())), - playersReady: t.Optional(t.Nullable(t.Record(t.String(), t.Boolean()))), - gameConfig: t.Optional( - t.Object({ game: t.String(), players: t.Array(t.String()) }) - ), - view: t.Optional(t.Any()), - results: t.Optional(t.Any()), -}); +export const WsOut = t.Union([ + t.Object({ playersPresent: t.Array(t.String()) }), + t.Object({ playerNames: t.Record(t.String(), t.String()) }), + t.Object({ playersReady: t.Nullable(t.Record(t.String(), t.Boolean())) }), + t.Object({ + gameConfig: t.Object({ game: t.String(), players: t.Array(t.String()) }), + }), + t.Object({ view: t.Any() }), + t.Object({ results: t.Any() }), +]); export type TWsOut = typeof WsOut.static; export const WsIn = t.Union([ t.Object({ name: t.String() }),