caravan-bitcoin/caravan

Improve Type System for `Caravan-Clients` Package

Aberta

#192 aberto em 14 de mar. de 2025

 (2 comentários) (0 reação) (0 responsável)TypeScript (102 forks)auto 404
@caravan/clientsdocumentationgood first issue

Métricas do repositório

Stars
 (85 estrelas)
Métricas de merge de PR
 (Métricas PR pendentes)

Description

Summary

After digging through the codebase, I've noticed our type system in the caravan-clients package has several inconsistencies that make development harder than it needs to be. I'd like to propose a series of improvements to make our types more consistent, reduce any usage, and better align with Bitcoin RPC responses.

The Problems

Looking at the code, I've noticed a few issues:

  • Types are all over the place - We have similar types defined in multiple files, and it's not clear which ones we should use.
  • Too many any types - We're using any in a lot of places with eslint-disable comments and @ts-expect-error workarounds.
  • Inconsistent parameter handling - Sometimes we pass arrays like [imports, { rescan }], other times objects like { minconf: 0, maxconf: 9999999, addresses: addressParam }.
  • Type assertions without validation - We often just assume what the response structure will be rather than properly typing it.
  • Each backend returns different formats - bitcoind, blockstream, and mempool all return different structures, making it hard to work with.

Proposed Approach

I think we should tackle this in stages through several PRs:

Tasks

1. Centralize Type Definitions

  • Create a logical organization structure for types.
  • Move shared types from various files to types.ts.
  • Create consistent naming conventions for types.
  • Document types with JSDoc comments.
  • Remove duplicate/redundant type definitions.

2. Improve RPC Parameter Handling

  • Create a flexible RPCParams type that can handle both array and object formats:
    export type RPCParam = string | number | boolean | null | undefined | RPCParam[] | { [key: string]: RPCParam };
    export type RPCParams = RPCParam[] | Record<string, RPCParam>;
    
  • Update RPC call functions to use these types.
  • Document parameter patterns for different RPC methods.
  • Update BitcoindWalletParams interface to use better typing.

3. Create Response Type Mapping

  • Create a reference table of Bitcoin RPC methods to TypeScript interfaces:
    RPC Method Documentation URL Response Type Currently Used In
    getwalletinfo Link WalletInfoResponse wallet.ts
    listunspent Link ListUnspentResponse[] wallet.ts
  • Define accurate response types based on Bitcoin Core documentation.
  • Create a type mapping between RPC methods and their response types:
    export interface RPCMethodResponseMap {
      getwalletinfo: WalletInfoResponse;
      listunspent: ListUnspentResponse[];
      // ...other methods
    }
    
  • Add version information to types for future maintenance.

4. Make Core Functions Generic

  • Update callBitcoindWallet to be generic (similar to callBitcoind):
    export function callBitcoindWallet<T>({
      baseUrl,
      walletName,
      auth,
      method,
      params,
    }: BitcoindWalletParams): Promise<RPCResponse<T>>
    
  • Fix the @ts-expect-error annotations in various functions.
  • Update function calls to use appropriate type parameters.
  • Implement method-specific type overloads where helpful.

5. Create Type Hierarchies for Complex Types

  • Establish clear hierarchies for related types (especially transaction data).
  • Create base interfaces that can be extended for specific use cases.
  • Document relationships between types.
  • Update code to use these hierarchies effectively.

6. Implement Response Normalization Pattern

  • Create normalized internal formats for key data structures.
  • Implement adapter functions for different backend responses.
  • Update client methods to use normalized formats.
  • Document the normalization patterns.

7. Replace any with Proper Type Guards

  • Replace any with unknown in error handling.
  • Add proper type guards for API responses and errors.
  • Create utility functions for common type checking patterns.
  • Remove eslint-disable comments where possible.

8. Documentation and Maintenance Strategy

  • Add comprehensive JSDoc comments to all types.
  • Document the type update process for future Bitcoin Core changes.
  • Create a version tracking strategy for type definitions.
  • Add example usage in comments for complex types.

Implementation Plan

I suggest we tackle this incrementally to avoid disrupting current development:

  1. First PR: Address items 1-2 (centralize types, fix RPC parameter handling).
  2. Second PR: Address items 3-4 (method mapping, generic functions).
  3. Third PR: Address items 5 (type hierarchies, replace any).
  4. Fourth PR: Address items 6 (normalisation functionality).
  5. Fifth PR: Address items 7 ( replace any).
  6. Sixth PR: Address item 8 (documentation and maintenance).

Why This Matters

These changes will:

  • Make it easier to understand the codebase.
  • Catch more errors at compile-time instead of runtime.
  • Improve IDE autocompletion and hints.
  • Make the codebase more maintainable.
  • Make it easier to update when Bitcoin Core changes.
  • Reduce the cognitive load for devs working on the project.

I'm happy to tackle the first PR to get things started. What do you all think :)

Guia do colaborador