Skip to content

Commit 987b3b2

Browse files
committed
infer data from primary tag
1 parent 37aa136 commit 987b3b2

File tree

8 files changed

+51
-20
lines changed

8 files changed

+51
-20
lines changed

examples/vite/src/examples/main/example.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,18 @@ export const builder = new HttpQueryBuilder().withClient(queryClient).withBaseUr
1212
export const resetMutation = builder.withPath('/reset').withMethod('post').withUpdates('*');
1313

1414
export const articlesQuery = builder
15-
.withTags('refreshable', 'articles')
15+
.withTags('articles', 'refreshable')
1616
.withPath('/articles')
17-
.withData<ArticleData[]>()
1817
.withSearch<{ page?: number }>()
1918
.withPagination({
2019
initialPageParam: { search: { page: 0 } },
2120
getNextPageParam: (prev, __, lastVars) => (!prev?.length ? null : { search: { page: (lastVars?.search?.page || 0) + 1 } }),
2221
});
2322

2423
export const { useQuery: useArticle, ...articleQuery } = builder
25-
.withTags('refreshable')
26-
.withPath('/articles/:id')
27-
.withData<ArticleData>()
24+
.withTags('article', 'refreshable')
2825
.withTags((ctx) => ({ type: 'article', id: ctx.data.id }))
26+
.withPath('/articles/:id')
2927
.withPreprocessor<{ id: number }>((vars) => ({ ...vars, params: { id: vars.id } }))
3028
.withMiddleware(async (ctx, next) => {
3129
const res = await next(ctx);

packages/tanstack-query-builder/src/builder/QueryBuilder.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { QueryClient } from '@tanstack/react-query';
22
import { operateOnTags } from '../tags/operateOnTags';
3-
import type { QueryTagObject, QueryTagOption, QueryUpdateTagObject } from '../tags/types';
3+
import type { InferDataFromQueryTagOption, QueryTagObject, QueryTagOption, QueryUpdateTagObject } from '../tags/types';
44
import { TODO } from '../type-utils';
55
import { QueryBuilderFrozen } from './QueryBuilderFrozen';
66
import { type MiddlewareFn, createMiddlewareFunction } from './createMiddlewareFunction';
@@ -112,15 +112,18 @@ export class QueryBuilder<
112112
* Adds a tag to the query.
113113
* The tag is used to invalidate or update the query when the tag is updated.
114114
*/
115-
withTags(...tags: QueryTagOption<TVars, TData, TError, QueryTagObject<TTags>>[]): this {
116-
return this.withMiddleware(createTagMiddleware(tags.flat(), QueryBuilder.tagCacheId++)) as unknown as this;
115+
withTags<const TTagOpt extends QueryTagOption<TVars, TData, TError, QueryTagObject<TTags>, TTags>>(
116+
primaryTag: TTagOpt,
117+
...otherTags: QueryTagOption<TVars, TData, TError, QueryTagObject<TTags>, TTags>[]
118+
): unknown extends TData ? QueryBuilder<TVars, InferDataFromQueryTagOption<TTagOpt, TTags>, TError, TKey, TTags, TFlags> : this {
119+
return this.withMiddleware(createTagMiddleware([primaryTag, ...otherTags].flat(), QueryBuilder.tagCacheId++)) as unknown as any;
117120
}
118121

119122
/**
120123
* Adds a declarative update to the mutation.
121124
* This is used to invalidate or update the queries that were marked with {@link withTags}.
122125
*/
123-
withUpdates(...tags: QueryTagOption<TVars, TData, TError, QueryUpdateTagObject<TVars, TData, TError, TTags>>[]): this {
126+
withUpdates(...tags: QueryTagOption<TVars, TData, TError, QueryUpdateTagObject<TVars, TData, TError, TTags>, TTags>[]): this {
124127
return this.withMiddleware(createUpdateMiddleware<TVars, TData, TError, TKey, TTags>(tags)) as unknown as this;
125128
}
126129

packages/tanstack-query-builder/src/builder/QueryBuilderClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class QueryBuilderClient<
4040
TError,
4141
TKey extends unknown[],
4242
TTags extends Record<string, unknown>,
43-
TFilters = QueryFilters<TData, TError, TData, TKey>,
43+
TFilters = QueryFilters<TKey>,
4444
> {
4545
private declare _options: BuilderConfig<TVars, TData, TError, TKey>['options'];
4646
private declare _pgOptions: BuilderConfig<TVars, TData, TError, TKey>['paginationOptions'];

packages/tanstack-query-builder/src/builder/createTagMiddleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { QueryTagCache, QueryTagObject, QueryTagOption } from '../tags/type
44
import type { MiddlewareFn } from './createMiddlewareFunction';
55

66
type CreateTagMiddleware = <TVars, TData, TError, TKey extends unknown[]>(
7-
tags: QueryTagOption<any, any, any>[],
7+
tags: QueryTagOption<any, any, any, any>[],
88
cacheId: string | number,
99
) => MiddlewareFn<TVars, TData, TError, TKey>;
1010

packages/tanstack-query-builder/src/builder/createUpdateMiddleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { type UpdateTagsUndoer, updateTags } from '../tags/updateTags';
55
import type { MiddlewareFn } from './createMiddlewareFunction';
66

77
type CreateUpdateMiddleware = <TVars, TData, TError, TKey extends unknown[], TTags extends Record<string, unknown>>(
8-
tags: QueryTagOption<TVars, TData, TError, QueryUpdateTagObject<TVars, TData, TError, TTags>>[],
8+
tags: QueryTagOption<TVars, TData, TError, QueryUpdateTagObject<TVars, TData, TError, TTags>, TTags>[],
99
) => MiddlewareFn<TVars, TData, TError, TKey>;
1010

1111
export const createUpdateMiddleware: CreateUpdateMiddleware = (tags) =>

packages/tanstack-query-builder/src/http/HttpQueryBuilder.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { QueryBuilder } from '../builder/QueryBuilder';
33
import type { BuilderPaginationOptions } from '../builder/options';
44
import type { BuilderConfig, BuilderFlags } from '../builder/types';
55
import type { RequestError } from '../http/errors';
6+
import { InferDataFromQueryTagOption, QueryTagObject, QueryTagOption } from '../tags/types';
67
import type { WithOptional } from '../type-utils';
78
import type { HttpBaseHeaders, HttpBaseParams, HttpBaseSearch, HttpBuilderVars } from './builder-types';
89
import { createHttpMergeVarsFn, createHttpQueryFn, createHttpQueryKeySanitizer } from './builder-utils';
@@ -128,6 +129,21 @@ export class HttpQueryBuilder<
128129
paginationOptions: BuilderPaginationOptions<HttpBuilderVars<TParam, TSearch, TBody, THeader, TMeta>, TData, TError, TKey>,
129130
) => HttpQueryBuilder<TParam, TSearch, TBody, THeader, TMeta, TData, TError, TTags, TFlags | 'withPagination', TKey>;
130131

132+
declare withTags: <
133+
const TTagOpt extends QueryTagOption<
134+
HttpBuilderVars<TParam, TSearch, TBody, THeader, TMeta>,
135+
TData,
136+
TError,
137+
QueryTagObject<TTags>,
138+
TTags
139+
>,
140+
>(
141+
primaryTag: TTagOpt,
142+
...otherTags: QueryTagOption<HttpBuilderVars<TParam, TSearch, TBody, THeader, TMeta>, TData, TError, QueryTagObject<TTags>, TTags>[]
143+
) => unknown extends TData
144+
? HttpQueryBuilder<TParam, TSearch, TBody, THeader, TMeta, InferDataFromQueryTagOption<TTagOpt, TTags>, TError, TTags, TFlags, TKey>
145+
: this;
146+
131147
withTagTypes<TTag extends string, T = unknown>(): HttpQueryBuilder<
132148
TParam,
133149
TSearch,

packages/tanstack-query-builder/src/tags/resolveTags.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { QueryClient } from '@tanstack/react-query';
22
import type { QueryTag, QueryTagContext, QueryTagObject, QueryTagOption } from './types';
33

44
export type ResolveTagsParams<TVars = void, TData = unknown, TErr = unknown, TTag extends QueryTagObject = QueryTagObject> = {
5-
tags?: QueryTagOption<TVars, TData, TErr, TTag> | QueryTagOption<TVars, TData, TErr, TTag>[] | null;
5+
tags?: QueryTagOption<TVars, TData, TErr, TTag, any> | QueryTagOption<TVars, TData, TErr, TTag, any>[] | null;
66
vars: TVars;
77
data?: TData;
88
error?: unknown;
@@ -21,7 +21,7 @@ export function resolveTags<TVars = void, TTag extends QueryTagObject = QueryTag
2121
}: ResolveTagsParams<TVars, any, any, TTag> & { client: QueryClient }): TTag[] {
2222
const resolvedTags =
2323
typeof tags === 'function'
24-
? resolveTags({ tags: tags(ctx as QueryTagContext<TVars>), ...ctx })
24+
? resolveTags({ tags: tags(ctx as QueryTagContext<TVars, any>), ...ctx })
2525
: Array.isArray(tags)
2626
? tags.flatMap((tag) => resolveTags({ tags: tag, ...ctx }))
2727
: // If there is an error, tags passed as array will be ignored. User should pass tags as function to handle error case if needed.

packages/tanstack-query-builder/src/tags/types.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,29 @@ export type QueryTagContext<TVars = void, TData = unknown, TErr = unknown> = {
2020
error?: TErr;
2121
};
2222

23-
export type QueryTagCallback<TVars = void, TData = unknown, TErr = unknown, TTag = QueryTag> = (
24-
ctx: QueryTagContext<TVars, TData, TErr>,
25-
) => TTag | readonly TTag[];
23+
export type QueryTagCallback<
24+
TVars = void,
25+
TData = unknown,
26+
TErr = unknown,
27+
TTag extends QueryTagObject<any> = QueryTagObject<any>,
28+
TTags extends TagMap = TagMap,
29+
> = (ctx: QueryTagContext<TVars, unknown extends TData ? InferDataFromQueryTagOption<TTag, TTags> : TData, TErr>) => TTag | readonly TTag[];
2630

2731
export type QueryTagStaticOption<TTag extends QueryTagObject<any> = QueryTagObject<any>> = '*' | QueryTag<TTag> | readonly QueryTag<TTag>[];
2832

29-
export type QueryTagOption<TVars = unknown, TData = unknown, TErr = unknown, TTag extends QueryTagObject<any> = QueryTagObject<any>> =
30-
| QueryTagStaticOption<TTag>
31-
| QueryTagCallback<TVars, TData, TErr, TTag>;
33+
export type QueryTagOption<
34+
TVars = unknown,
35+
TData = unknown,
36+
TErr = unknown,
37+
TTag extends QueryTagObject<any> = QueryTagObject<any>,
38+
TTags extends TagMap = TagMap,
39+
> = QueryTagStaticOption<TTag> | QueryTagCallback<TVars, TData, TErr, TTag, TTags>;
40+
41+
export type InferDataFromQueryTagOption<TTag extends QueryTagOption<any, any, any, any>, TTags extends TagMap> = TTag extends string
42+
? TTags[TTag]
43+
: TTag extends QueryTagCallback<any, infer TData, any, any>
44+
? TData
45+
: unknown;
3246

3347
export type QueryUpdaterFn<TVars = unknown, TData = unknown, TErr = unknown, TTarget = unknown> = (
3448
ctx: QueryTagContext<TVars, TData, TErr>,

0 commit comments

Comments
 (0)