Skip to content

Commit

Permalink
feat: use StoreName for improved type experience (#350)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranet committed Nov 16, 2023
1 parent 7b0ae1d commit 3c722ef
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 40 deletions.
16 changes: 8 additions & 8 deletions src/lib/structures/AliasPiece.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Piece } from './Piece';
import type { StoreRegistryEntries } from './StoreRegistry';
import type { StoreRegistryKey } from './StoreRegistry';

export interface AliasPieceOptions extends Piece.Options {
/**
Expand All @@ -12,24 +12,24 @@ export interface AliasPieceOptions extends Piece.Options {
/**
* The piece to be stored in {@link AliasStore} instances.
*/
export class AliasPiece<
Options extends AliasPieceOptions = AliasPieceOptions,
StoreName extends keyof StoreRegistryEntries = keyof StoreRegistryEntries
> extends Piece<Options, StoreName> {
export class AliasPiece<Options extends AliasPieceOptions = AliasPieceOptions, StoreName extends StoreRegistryKey = StoreRegistryKey> extends Piece<
Options,
StoreName
> {
/**
* The aliases for the piece.
*/
public aliases: readonly string[];

public constructor(context: Piece.Context, options: AliasPieceOptions = {}) {
public constructor(context: AliasPiece.Context, options: AliasPieceOptions = {}) {
super(context, options);
this.aliases = options.aliases ?? [];
}

/**
* Defines the `JSON.stringify` behavior of this alias piece.
*/
public override toJSON(): AliasPieceJSON {
public override toJSON(): AliasPiece.JSON {
return {
...super.toJSON(),
aliases: this.aliases.slice()
Expand All @@ -48,7 +48,7 @@ export interface AliasPieceJSON extends Piece.JSON {
export namespace AliasPiece {
export const { Location } = Piece;
export type Options = AliasPieceOptions;
export type Context = Piece.Context;
export type Context<StoreName extends StoreRegistryKey = StoreRegistryKey> = Piece.Context<StoreName>;
export type JSON = AliasPieceJSON;
export type LocationJSON = Piece.LocationJSON;
}
4 changes: 2 additions & 2 deletions src/lib/structures/AliasStore.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Collection } from '@discordjs/collection';
import type { AliasPiece } from './AliasPiece';
import { Store } from './Store';
import type { StoreRegistryEntries } from './StoreRegistry';
import type { StoreRegistryKey } from './StoreRegistry';

/**
* The store class which contains {@link AliasPiece}s.
*/
export class AliasStore<T extends AliasPiece, StoreName extends keyof StoreRegistryEntries = keyof StoreRegistryEntries> extends Store<T, StoreName> {
export class AliasStore<T extends AliasPiece, StoreName extends StoreRegistryKey = StoreRegistryKey> extends Store<T, StoreName> {
/**
* The aliases referencing to pieces.
*/
Expand Down
12 changes: 6 additions & 6 deletions src/lib/structures/Piece.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import type { Awaitable } from '@sapphire/utilities';
import { container, type Container } from '../shared/Container';
import { PieceLocation, type PieceLocationJSON } from './PieceLocation';
import type { Store } from './Store';
import type { StoreOf, StoreRegistryEntries } from './StoreRegistry';
import type { StoreOf, StoreRegistryKey } from './StoreRegistry';

/**
* The context for the piece, contains extra information from the store,
* the piece's path, and the store that loaded it.
*/
export interface PieceContext {
export interface PieceContext<StoreName extends StoreRegistryKey = StoreRegistryKey> {
/**
* The root directory the piece was loaded from.
*/
Expand All @@ -27,7 +27,7 @@ export interface PieceContext {
/**
* The store that loaded the piece.
*/
readonly store: Store<Piece>;
readonly store: StoreOf<StoreName>;
}

/**
Expand All @@ -50,7 +50,7 @@ export interface PieceOptions {
/**
* The piece to be stored in {@link Store} instances.
*/
export class Piece<Options extends PieceOptions = PieceOptions, StoreName extends keyof StoreRegistryEntries = keyof StoreRegistryEntries> {
export class Piece<Options extends PieceOptions = PieceOptions, StoreName extends StoreRegistryKey = StoreRegistryKey> {
/**
* The store that contains the piece.
*/
Expand All @@ -76,7 +76,7 @@ export class Piece<Options extends PieceOptions = PieceOptions, StoreName extend
*/
public readonly options: Options;

public constructor(context: PieceContext, options: PieceOptions = {}) {
public constructor(context: PieceContext<StoreName>, options: PieceOptions = {}) {
this.store = context.store;
this.location = new PieceLocation(context.path, context.root);
this.name = options.name ?? context.name;
Expand Down Expand Up @@ -149,7 +149,7 @@ export interface PieceJSON {
export namespace Piece {
export const Location = PieceLocation;
export type Options = PieceOptions;
export type Context = PieceContext;
export type Context<StoreName extends StoreRegistryKey = StoreRegistryKey> = PieceContext<StoreName>;
export type JSON = PieceJSON;
export type LocationJSON = PieceLocationJSON;
}
16 changes: 8 additions & 8 deletions src/lib/structures/Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import { container, type Container } from '../shared/Container';
import type { HydratedModuleData, ILoaderResultEntry, ILoaderStrategy, ModuleData } from '../strategies/ILoaderStrategy';
import { LoaderStrategy } from '../strategies/LoaderStrategy';
import type { Piece } from './Piece';
import { StoreRegistry, type StoreRegistryEntries } from './StoreRegistry';
import { StoreRegistry, StoreRegistryKey, type StoreRegistryEntries } from './StoreRegistry';

/**
* The options for the store, this features both hooks (changes the behaviour) and handlers (similar to event listeners).
*/
export interface StoreOptions<T extends Piece> {
export interface StoreOptions<T extends Piece, StoreName extends StoreRegistryKey = StoreRegistryKey> {
/**
* The name for this store.
*/
readonly name: string;
readonly name: StoreName;

/**
* The paths to load pieces from, should be absolute.
Expand Down Expand Up @@ -51,9 +51,9 @@ export interface StoreLogger {
/**
* The store class which contains {@link Piece}s.
*/
export class Store<T extends Piece, StoreName extends keyof StoreRegistryEntries = keyof StoreRegistryEntries> extends Collection<string, T> {
export class Store<T extends Piece, StoreName extends StoreRegistryKey = StoreRegistryKey> extends Collection<string, T> {
public readonly Constructor: AbstractConstructor<T>;
public readonly name: keyof StoreRegistryEntries;
public readonly name: StoreName;
public readonly paths: Set<string>;
public readonly strategy: ILoaderStrategy<T>;

Expand All @@ -71,10 +71,10 @@ export class Store<T extends Piece, StoreName extends keyof StoreRegistryEntries
* @param constructor The piece constructor this store loads.
* @param options The options for the store.
*/
public constructor(constructor: AbstractConstructor<T>, options: StoreOptions<T>) {
public constructor(constructor: AbstractConstructor<T>, options: StoreOptions<T, StoreName>) {
super();
this.Constructor = constructor;
this.name = options.name as keyof StoreRegistryEntries;
this.name = options.name as StoreRegistryKey;
this.paths = new Set(options.paths ?? []);
this.strategy = options.strategy ?? Store.defaultStrategy;
}
Expand Down Expand Up @@ -396,7 +396,7 @@ export class Store<T extends Piece, StoreName extends keyof StoreRegistryEntries
* An entry for a manually registered piece using {@linkcode Store.loadPiece()}.
* @since 3.8.0
*/
export interface StoreManuallyRegisteredPiece<StoreName extends keyof StoreRegistryEntries> {
export interface StoreManuallyRegisteredPiece<StoreName extends StoreRegistryKey> {
name: string;
piece: StoreRegistryEntries[StoreName] extends Store<infer Piece> ? Constructor<Piece> : never;
}
Expand Down
40 changes: 24 additions & 16 deletions src/lib/structures/StoreRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import { ManuallyRegisteredPiecesSymbol, VirtualPath } from '../internal/constan
import type { Piece } from './Piece';
import type { Store, StoreManuallyRegisteredPiece } from './Store';

type Key = keyof StoreRegistryEntries;
type Value = StoreRegistryEntries[Key];

/**
* A strict-typed store registry. This is available in {@link container}.
* @since 2.1.0
Expand All @@ -31,14 +28,11 @@ type Value = StoreRegistryEntries[Key];
* }
* ```
*/
export class StoreRegistry extends Collection<Key, Value> {
export class StoreRegistry extends Collection<StoreRegistryKey, StoreRegistryValue> {
/**
* The queue of pieces to load.
*/
readonly #pendingManuallyRegisteredPieces = new Collection<
keyof StoreRegistryEntries,
StoreManuallyRegisteredPiece<keyof StoreRegistryEntries>[]
>();
readonly #pendingManuallyRegisteredPieces = new Collection<StoreRegistryKey, StoreManuallyRegisteredPiece<StoreRegistryKey>[]>();

/**
* Loads all the registered stores.
Expand Down Expand Up @@ -109,7 +103,7 @@ export class StoreRegistry extends Collection<Key, Value> {
* @param store The store to register.
*/
public register<T extends Piece>(store: Store<T>): this {
this.set(store.name as Key, store as unknown as Value);
this.set(store.name as StoreRegistryKey, store as unknown as StoreRegistryValue);

// If there was a queue for this store, add it to the store and delete the queue:
const queue = this.#pendingManuallyRegisteredPieces.get(store.name);
Expand All @@ -127,7 +121,7 @@ export class StoreRegistry extends Collection<Key, Value> {
* @param store The store to deregister.
*/
public deregister<T extends Piece>(store: Store<T>): this {
this.delete(store.name as Key);
this.delete(store.name as StoreRegistryKey);
return this;
}

Expand Down Expand Up @@ -168,7 +162,7 @@ export class StoreRegistry extends Collection<Key, Value> {
* });
* ```
*/
public async loadPiece<StoreName extends keyof StoreRegistryEntries>(entry: StoreManagerManuallyRegisteredPiece<StoreName>) {
public async loadPiece<StoreName extends StoreRegistryKey>(entry: StoreManagerManuallyRegisteredPiece<StoreName>) {
const store = this.get(entry.store) as Store<Piece, StoreName> | undefined;

if (store) {
Expand All @@ -184,12 +178,24 @@ export class StoreRegistry extends Collection<Key, Value> {
}

export interface StoreRegistry {
get<K extends Key>(key: K): StoreRegistryEntries[K];
get<K extends StoreRegistryKey>(key: K): StoreRegistryEntries[K];
get(key: string): undefined;
has(key: Key): true;
has(key: StoreRegistryKey): true;
has(key: string): false;
}

/**
* A type utility to get the keys of {@linkcode StoreRegistryEntries}.
* @since 3.10.0
*/
export type StoreRegistryKey = keyof StoreRegistryEntries;

/**
* A type utility to get the values of {@linkcode StoreRegistryEntries}.
* @since 3.10.0
*/
export type StoreRegistryValue = StoreRegistryEntries[StoreRegistryKey];

/**
* The {@link StoreRegistry}'s registry, use module augmentation against this interface when adding new stores.
* @since 2.1.0
Expand All @@ -201,21 +207,23 @@ export interface StoreRegistryEntries {}
* @seealso {@linkcode StoreRegistry.loadPiece()}
* @since 3.8.0
*/
export interface StoreManagerManuallyRegisteredPiece<StoreName extends keyof StoreRegistryEntries> extends StoreManuallyRegisteredPiece<StoreName> {
export interface StoreManagerManuallyRegisteredPiece<StoreName extends StoreRegistryKey> extends StoreManuallyRegisteredPiece<StoreName> {
store: StoreName;
}

/**
* Type utility to get the {@linkcode Store} given its name.
* @since 3.10.0
*/
export type StoreOf<StoreName extends keyof StoreRegistryEntries> = keyof StoreRegistryEntries extends never
export type StoreOf<StoreName extends StoreRegistryKey> = StoreRegistryKey extends never
? Store<Piece<Piece.Options, StoreName>>
: StoreRegistryEntries[StoreName];

/**
* Type utility to get the {@linkcode Piece} given its {@linkcode Store}'s name.
* @since 3.10.0
*/
export type PieceOf<StoreName extends keyof StoreRegistryEntries> = keyof StoreRegistryEntries extends never
export type PieceOf<StoreName extends StoreRegistryKey> = StoreRegistryKey extends never
? Piece<Piece.Options, StoreName>
: StoreRegistryEntries[StoreName] extends Store<infer PieceType>
? PieceType
Expand Down

0 comments on commit 3c722ef

Please sign in to comment.