From fb3c98b1ef93f84aa052d6869d471d296da21b05 Mon Sep 17 00:00:00 2001 From: hyper Date: Thu, 23 Apr 2026 16:03:49 +0800 Subject: [PATCH] refactor: update GameHost to use generic state and result types Decouple GameHost from GameState and GameResult types by introducing generic parameters for TState and TResult. This improves type safety and flexibility when defining GameModules. --- src/core/game-host.ts | 41 ++++++++++++++++++----------------------- src/index.ts | 8 +------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/core/game-host.ts b/src/core/game-host.ts index 625dce4..d811ca6 100644 --- a/src/core/game-host.ts +++ b/src/core/game-host.ts @@ -1,29 +1,28 @@ -import { ReadonlySignal, Signal } from "@preact/signals-core"; import { createPromptContext } from "@/utils/command"; -import { createGameContext, IGameContext } from "./game"; import { PromptContext } from "@/utils/command/command-prompt"; +import { ReadonlySignal, Signal } from "@preact/signals-core"; +import { createGameContext, IGameContext } from "./game"; export type GameHostStatus = "created" | "running" | "disposed"; -export class GameHost { - readonly state: ReadonlySignal>; +export class GameHost< + TState extends Record, + TResult = unknown, + TModule extends GameModule = GameModule, +> { + readonly state: ReadonlySignal; readonly status: ReadonlySignal; readonly prompts: PromptContext; - private _context: IGameContext>; - private _start: ( - ctx: IGameContext>, - ) => Promise>; + private _context: IGameContext; + private _start: (ctx: IGameContext) => Promise; private _status: Signal; - private _createInitialState: () => GameState; + private _createInitialState: () => TState; private _eventListeners: Map<"start" | "dispose", Set<() => void>>; private _isDisposed = false; constructor(public readonly gameModule: TModule) { - const { createInitialState, start } = gameModule as unknown as GameModule< - GameState, - GameResult - >; + const { createInitialState, start } = gameModule; this._createInitialState = createInitialState; this._eventListeners = new Map(); @@ -53,7 +52,7 @@ export class GameHost { this._context._state.clearInterruptions(); } - start(seed?: number): Promise> { + start(seed?: number): Promise { if (this._isDisposed) { throw new Error("GameHost is disposed"); } @@ -115,15 +114,11 @@ export type GameModule< createInitialState: () => TState; start: (ctx: IGameContext) => Promise; }; -export type GameState = ReturnType< - TModule["createInitialState"] ->; -export type GameResult = Awaited< - ReturnType ->; -export function createGameHost( - gameModule: TModule, -): GameHost { +export function createGameHost< + TState extends Record = Record, + TResult = unknown, + TModule extends GameModule = GameModule, +>(gameModule: TModule): GameHost { return new GameHost(gameModule); } diff --git a/src/index.ts b/src/index.ts index 603a531..2d411d1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,13 +7,7 @@ export type { IGameContext } from "./core/game"; export { createPromptDef } from "./core/game"; -export type { - GameHost, - GameHostStatus, - GameModule, - GameState, - GameResult, -} from "./core/game-host"; +export type { GameHost, GameHostStatus, GameModule } from "./core/game-host"; export { createGameHost } from "./core/game-host"; export type { Part } from "./core/part";