Advanced

Custom Structures & Types

Override built-in structures and use inferred structure types.

Hoshimi allows you to replace default classes with custom implementations. This guide covers both overriding structures and using their inferred types.

Basic Override Pattern

Create a custom subclass and assign it to the Structures factory.

custom-player.ts
import { ,  } from 'hoshimi';

class  extends  {
  public (): boolean {
    return this. > 120;
  }
}

. = (...) => new (...);

declare module 'hoshimi' {
  interface CustomizableStructures {
    : ;
  }
}

The augmentation is what re-types everything

Assigning to Structures swaps the class at runtime; augmenting CustomizableStructures is what makes player.filterManager, PlayerStructure and every other reference resolve to your subclass. A local interface CustomizableStructures in your file does nothing — it has to be inside declare module 'hoshimi'.

Multiple Overrides

Override several structures at once for comprehensive customization.

multi-override.ts
import { , , ,  } from 'hoshimi';

class  extends  {}
class  extends  {}
class  extends  {}

. = (...) => new (...);
. = (...) => new (...);
. = (...) => new (...);

declare module 'hoshimi' {
  interface CustomizableStructures {
    : ;
    : ;
    : ;
  }
}

Every structure is overridable: Player, Node, NodeManager, Rest, Queue, LyricsManager, FilterManager, Track, UnresolvedTrack, PlayerVoiceState and PlayerStorageAdapter.

Using Structure Types

The *Structure type aliases represent the final instance types. Use them in function signatures for flexibility.

structure-types-usage.ts
import type { ,  } from 'hoshimi';

function (: ): void {
  // Works with default Player or any custom Player replacement
  .(`Guild: ${.}`);
}

function (: ): string {
  return ..;
}

Module Augmentation for Custom Data

Extend built-in structures with custom properties via module augmentation.

custom-data-types.ts
import type { LyricsResult } from 'hoshimi';

declare module 'hoshimi' {
  interface CustomizablePlayerStorage {
    : boolean;
    : boolean;
    : string;
    : LyricsResult;
  }

  interface CustomizableTrack {
    : {
      : string;
      : string;
    };
  }
}

Best Practices

  • Override structures once during bootstrap (before creating any managers).
  • Keep constructor signatures unchanged to maintain compatibility.
  • Add narrow, domain-specific methods instead of rewriting core flow logic.
  • Use *Structure types in function signatures to avoid coupling to concrete classes.
  • Keep extensions strongly typed after overrides with InferCustomStructure.