Skip to content

Commit

Permalink
Refactor Model+QueryBuilder types for single fetch
Browse files Browse the repository at this point in the history
• add an IsSingle type argument to QueryBuilder to track if first() has been invoked
• add the toJSON method to QueryBuilder
• update return types of BaseModel query methods to return a QueryBuilder
• update QueryBuilder promise methods (then/catch) to resolve query results based on if IsSingle (i.e. if first() has been invoked) or not
  • Loading branch information
acusti committed Jan 22, 2025
1 parent 4e94c50 commit b63ea4f
Showing 1 changed file with 48 additions and 19 deletions.
67 changes: 48 additions & 19 deletions packages/superflare/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,12 @@ export interface HasMany<M extends BaseModel, R = InstanceType<M>[]>
* Shape of the model constructor (static properties).
*/
export interface BaseModel<M = any> {
find<T extends BaseModel>(this: T, ids: number[]): Promise<InstanceType<T>[]>;
find<T extends BaseModel>(this: T, ids: number[]): QueryBuilder<T>;
find<T extends BaseModel>(
this: T,
id: number
): Promise<null | InstanceType<T>>;
first<T extends BaseModel>(this: T): Promise<null | InstanceType<T>>;
orderBy<T extends BaseModel>(
this: T,
field: string,
direction?: "asc" | "desc"
): QueryBuilder<T>;
all<T extends BaseModel>(this: T): Promise<InstanceType<T>[]>;
): QueryBuilder<T, InstanceType<T>, true>;
all<T extends BaseModel>(this: T): QueryBuilder<T>;
where<T extends BaseModel>(
this: T,
field: string,
Expand All @@ -101,12 +95,21 @@ export interface BaseModel<M = any> {
this: T,
relationName: string | string[]
): QueryBuilder<T>;
orderBy<T extends BaseModel>(
this: T,
field: string,
direction?: "asc" | "desc"
): QueryBuilder<T>;
query<T extends BaseModel>(this: T): QueryBuilder<T>;
// Changes return type to single instance:
first<T extends BaseModel>(this: T): QueryBuilder<T, InstanceType<T>, true>;
count<T extends BaseModel>(this: T): Promise<number>;

create<T extends BaseModel>(
this: T,
attributes: any
): Promise<InstanceType<T>>;
count<T extends BaseModel>(this: T): Promise<number>;
query<T extends BaseModel>(this: T): QueryBuilder<T>;

register(model: any): void;

tableName: string;
Expand All @@ -115,20 +118,46 @@ export interface BaseModel<M = any> {
new (attributes?: any): ModelInstance<M>;
}

interface QueryBuilder<T extends BaseModel, R = InstanceType<T>> {
count<T>(this: T): Promise<number>;
find<T>(this: T, id: number): Promise<null | R>;
find<T>(this: T, ids: number[]): Promise<R[]>;
// Helper type to extract the JSON return type from a model instance
type JSONReturnType<I> = I extends ModelInstance<any>
? ReturnType<I["toJSON"]>
: never;

// Helper type to determine if the query will return a single item or array
type QueryResult<I, IsSingle extends boolean> = IsSingle extends true ? I : I[];

interface QueryBuilder<
M extends BaseModel,
I = InstanceType<M>,
IsSingle extends boolean = false
> {
find<T>(this: T, id: number): QueryBuilder<M, I, true>;
find<T>(this: T, ids: number[]): this;
where<T>(this: T, field: string, value: any): this;
where<T>(this: T, field: string, operator: string, value?: any): this;
whereIn<T>(this: T, field: string, values: (string | number)[]): this;
with<T>(this: T, relationName: string | string[]): this;
limit<T>(this: T, limit: number): this;
get(): Promise<R[]>;
first(): Promise<null | R>;
orderBy<T>(this: T, field: string, direction?: "asc" | "desc"): this;
then(onfulfilled?: (value: R[]) => R[] | PromiseLike<R[]>): Promise<R[]>;
catch(onrejected?: (reason: any) => any): Promise<R[]>;
// Changes return type to single instance:
first(): QueryBuilder<M, I, true>;

// Promise-returning methods with specific return types
count<T>(this: T): Promise<number>;
get(): Promise<QueryResult<I, IsSingle>>;
toJSON(): Promise<
IsSingle extends true ? JSONReturnType<I> : JSONReturnType<I>[]
>;

// Promise compatibility
then<Result = QueryResult<I, IsSingle>>(
onfulfilled?: (
value: QueryResult<I, IsSingle>
) => Result | PromiseLike<Result>
): Promise<Result>;
catch<Result = QueryResult<I, IsSingle>>(
onrejected?: ((reason: any) => Result | PromiseLike<Result>) | null
): Promise<Result>;
}

declare const Model: BaseModel;
Expand Down

0 comments on commit b63ea4f

Please sign in to comment.