diff --git a/resourcerer.d.ts b/resourcerer.d.ts index 46333b9..519d2d1 100644 --- a/resourcerer.d.ts +++ b/resourcerer.d.ts @@ -10,9 +10,19 @@ type GetModelOptions = : never; declare module "resourcerer" { - type WithModelSuffix = + type WithModelSuffix = C extends Collection ? `${K}Collection` : `${K}Model`; export type LoadingStates = "error" | "loading" | "loaded" | "pending"; + + // empty interface for declaration merging - users extend this with their model definitions + // @ts-ignore + export interface ModelMap {} + export type ResourceKeys = Extract; + + // Resource config for a specific resource key + // K represents the ResourceKey, which can be either: + // - The key itself (when the key IS a ResourceKey) + // - The resourceKey field value (when using a custom key with resourceKey property) export type ResourceConfigObj = { data?: Partial>[0]>; dependsOn?: boolean; @@ -27,15 +37,69 @@ declare module "resourcerer" { prefetches?: { [key: string]: any }[]; provides?: ( model: InstanceType, - props: Record + props: Record, ) => { [key: string]: any }; }; - export interface ModelMap {} - export type ResourceKeys = Extract; + // Helper type to create a config object with resourceKey field + // Used when the executor key is not a ResourceKey and needs a resourceKey property + type ResourceConfigWithKey = ResourceConfigObj & { + resourceKey: RK; + }; + + // Helper to extract resourceKey from a config object + // If the config has a resourceKey property, extract it + // Otherwise, if the config is ResourceConfigObj, K is the resourceKey + type ExtractResourceKey = + Config extends { resourceKey: infer RK } ? + RK extends ResourceKeys ? + RK + : never + : never; + + // Helper to get the resource key for a given key using the executor return type + // If the key is a ResourceKeys, use it directly + // Otherwise, extract the resourceKey from the config + type GetResourceKey> = + K extends ResourceKeys ? K + : K extends keyof R ? + R[K] extends infer Config ? + Config extends undefined ? + never + : ExtractResourceKey + : never + : never; + + // Helper to get the model constructor type for a given key + type GetModelConstructor> = + GetResourceKey extends keyof ModelMap ? ModelMap[GetResourceKey] : never; + + // Helper to get the model instance type for a given key + type GetModelType> = + GetModelConstructor extends new (...args: any[]) => infer ModelType ? ModelType : never; + + // Union type of all possible configs with resourceKey + // TypeScript will narrow this discriminated union based on the literal resourceKey value + // When you use resourceKey: "energySource", provides will be typed for energySourceModel + type ConfigWithResourceKey = { + [RK in ResourceKeys]: ResourceConfigWithKey; + }[ResourceKeys]; - export type ExecutorFunction> = (props: O) => { - [Key in T]?: ResourceConfigObj; + // ExecutorFunction with explicit resourceKey mapping + // M maps executor keys to their resourceKeys (e.g., { gridEnergySource: "energySource" }) + // When a custom key is used with resourceKey property, TypeScript will narrow the provides type + // based on the literal resourceKey value provided + export type ExecutorFunction< + T extends string, + O = Record, + M extends Partial> = Partial>, + > = (props: O) => { + [Key in T]?: Key extends ResourceKeys ? ResourceConfigObj + : Key extends keyof M ? + M[Key] extends ResourceKeys ? + ResourceConfigWithKey + : ConfigWithResourceKey + : ConfigWithResourceKey; }; export type UseResourcesResponse = { @@ -48,14 +112,25 @@ declare module "resourcerer" { setResourceState(newState: { [key: string]: any }): void; }; - export function useResources>( - getResources: ExecutorFunction, - _props: O + export function useResources< + O extends Record, + F extends (props: O) => Record, + T extends string = Extract, string>, + R extends ReturnType = ReturnType, + >( + getResources: F, + _props: O, ): UseResourcesResponse & { - [Key in T as WithModelSuffix>]: InstanceType; + [Key in T as GetResourceKey extends ResourceKeys ? + GetModelType extends infer C ? + C extends Collection ? + `${Key}Collection` + : `${Key}Model` + : `${Key}Model` + : never]: GetModelType; } & { - [Key in T as `${T}LoadingState`]: LoadingStates; + [Key in T as `${Key}LoadingState`]: LoadingStates; } & { - [Key in T as `${T}Status`]: number; + [Key in T as `${Key}Status`]: number; }; }