From c915c21d47a2b417979b20e9e9d9b6ff30a03c0d Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 31 Dec 2025 19:51:21 +0900 Subject: refactor(client): use Hono InferResponseType for API response types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace manually defined AuthResponse and User types with Hono's InferResponseType to automatically derive types from server definitions. This ensures client types stay in sync with server responses. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/client/api/client.test.ts | 30 ++++++++++++++++++++++++++++++ src/client/api/client.ts | 15 +++++++++++---- src/client/api/index.ts | 4 +++- src/client/api/types.ts | 11 ----------- 4 files changed, 44 insertions(+), 16 deletions(-) (limited to 'src/client/api') diff --git a/src/client/api/client.test.ts b/src/client/api/client.test.ts index 5489547..27c3a0a 100644 --- a/src/client/api/client.test.ts +++ b/src/client/api/client.test.ts @@ -13,8 +13,10 @@ import { import { ApiClient, ApiClientError, + type LoginResponse, localStorageTokenStorage, type TokenStorage, + type User, } from "./client"; function createMockTokenStorage(): TokenStorage & { @@ -209,3 +211,31 @@ describe("localStorageTokenStorage", () => { expect(localStorageTokenStorage.getTokens()).toBeNull(); }); }); + +describe("InferResponseType types", () => { + it("LoginResponse has expected properties", () => { + // This test verifies the inferred types have the correct structure + // The type assertions will fail at compile time if the types are wrong + const mockResponse: LoginResponse = { + accessToken: "access-token", + refreshToken: "refresh-token", + user: { id: "123", username: "testuser" }, + }; + + expect(mockResponse.accessToken).toBe("access-token"); + expect(mockResponse.refreshToken).toBe("refresh-token"); + expect(mockResponse.user.id).toBe("123"); + expect(mockResponse.user.username).toBe("testuser"); + }); + + it("User type is correctly derived from LoginResponse", () => { + // Verify User type has expected structure + const user: User = { + id: "user-1", + username: "testuser", + }; + + expect(user.id).toBe("user-1"); + expect(user.username).toBe("testuser"); + }); +}); diff --git a/src/client/api/client.ts b/src/client/api/client.ts index 7741942..7607eb6 100644 --- a/src/client/api/client.ts +++ b/src/client/api/client.ts @@ -1,6 +1,13 @@ -import { hc } from "hono/client"; +import { hc, type InferResponseType } from "hono/client"; import type { AppType } from "../../server/index.js"; -import type { ApiError, AuthResponse, Tokens } from "./types"; +import type { ApiError, Tokens } from "./types"; + +// Create a temporary client just for type inference +const _rpc = hc(""); + +// Infer response types from server definitions +export type LoginResponse = InferResponseType; +export type User = LoginResponse["user"]; export class ApiClientError extends Error { constructor( @@ -120,12 +127,12 @@ export class ApiClient { } } - async login(username: string, password: string): Promise { + async login(username: string, password: string): Promise { const res = await this.rpc.api.auth.login.$post({ json: { username, password }, }); - const data = await this.handleResponse(res); + const data = await this.handleResponse(res); this.tokenStorage.setTokens({ accessToken: data.accessToken, diff --git a/src/client/api/index.ts b/src/client/api/index.ts index fb26b70..63d0a40 100644 --- a/src/client/api/index.ts +++ b/src/client/api/index.ts @@ -3,7 +3,9 @@ export { ApiClientError, type ApiClientOptions, apiClient, + type LoginResponse, localStorageTokenStorage, type TokenStorage, + type User, } from "./client"; -export type { ApiError, AuthResponse, Tokens, User } from "./types"; +export type { ApiError, Tokens } from "./types"; diff --git a/src/client/api/types.ts b/src/client/api/types.ts index eaf69eb..70918fe 100644 --- a/src/client/api/types.ts +++ b/src/client/api/types.ts @@ -1,14 +1,3 @@ -export interface User { - id: string; - username: string; -} - -export interface AuthResponse { - accessToken: string; - refreshToken: string; - user: User; -} - export interface ApiError { error: { message: string; -- cgit v1.2.3-70-g09d2