Skip to content

Commit

Permalink
default to Query<any>
Browse files Browse the repository at this point in the history
  • Loading branch information
ptpaterson committed Jul 24, 2024
1 parent 1d1ac81 commit 8facc3b
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 18 deletions.
17 changes: 7 additions & 10 deletions __tests__/integration/query-typings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ describe.each`
const paginatedQuery = fql`[{ "x": 123}].toSet()`;

if ("query" === method) {
expect.assertions(1);
const result = (await client.query<MyType>(query)).data;
expect(result).toEqual({ x: 123 });
} else {
Expand All @@ -43,23 +42,16 @@ describe.each`
});

it("allows customers to infer their own types in queries from fql statements", async () => {
// This is a noop function that is only used to validate the inferred type of the query
// It will fail at build time if types are not inferred correctly.
const noopToValidateInferredType = (value: MyType) => {};

const query = fql<MyType>`{ "x": 123 }`;
const paginatedQuery = fql<Page<MyType>>`[{ "x": 123}].toSet()`;

if ("query" === method) {
expect.assertions(1);
const result = (await client.query(query)).data;
noopToValidateInferredType(result);
expect(result).toEqual({ x: 123 });
} else {
expect.assertions(2);
for await (const page of client.paginate(paginatedQuery)) {
for (const result of page) {
noopToValidateInferredType(result);
expect(result).toEqual({ x: 123 });
}
}
Expand All @@ -69,11 +61,16 @@ describe.each`
// exactly one item is returned.
for await (const page of client.paginate(query)) {
for (const result of page) {
noopToValidateInferredType(result);
expect(result).toEqual({ x: 123 });
}
}
}
Promise.resolve();
});

it("allows customers to use subtyped queries", async () => {
const query = fql<string>`"hello"`;

const result = (await client.query<string | number>(query)).data;
expect(result).toEqual("hello");
});
});
7 changes: 6 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,12 @@ export class Client {
* ```
*/
paginate<T extends QueryValue>(
iterable: Page<T> | EmbeddedSet | Query<Page<T>> | Query<T>,
iterable:
| Page<T>
| EmbeddedSet
| Query<Page<T>>
| Query<T>
| Query<T | Page<T>>, // needed for inference when using the `fql` function within `.paginate()`, e.g. client.paginate<MyType>(fql`...`)
options?: QueryOptions,
): SetIterator<T> {
if (iterable instanceof Query) {
Expand Down
26 changes: 19 additions & 7 deletions src/query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type QueryArgumentObject = {
*/
export type QueryArgument =
| QueryValue
| Query
| Query<any>
| Date
| ArrayBuffer
| Uint8Array
Expand All @@ -46,7 +46,7 @@ export type QueryArgument =
* const queryRequestBuilder = fql`${str}.length == ${innerQuery}`;
* ```
*/
export function fql<T extends QueryValue = QueryValue>(
export function fql<T extends QueryValue = any>(
queryFragments: ReadonlyArray<string>,
...queryArgs: QueryArgument[]
): Query<T> {
Expand All @@ -61,13 +61,22 @@ export function fql<T extends QueryValue = QueryValue>(
* T can be used to infer the type of the response type from {@link Client}
* methods.
*/
// HACK: We need to disable the next line because the type param is never
// explicitly used in the class. It is inferred by the constructor and
// used as the return type. You cannot annotate the constructor return type.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class Query<T extends QueryValue = QueryValue> {
export class Query<T extends QueryValue = any> {
readonly #queryFragments: ReadonlyArray<string>;
readonly #interpolatedArgs: QueryArgument[];
/**
* A phantom field to enforce the type of the Query.
* @internal
*
* We need to provide an actual property of type `T` for Typescript to
* actually enforce it.
*
* "Because TypeScript is a structural type system, type parameters only
* affect the resulting type when consumed as part of the type of a member."
*
* @see {@link https://www.typescriptlang.org/docs/handbook/type-compatibility.html#generics}
*/
readonly #__phantom: T;

constructor(
queryFragments: ReadonlyArray<string>,
Expand All @@ -81,6 +90,9 @@ export class Query<T extends QueryValue = QueryValue> {
}
this.#queryFragments = queryFragments;
this.#interpolatedArgs = queryArgs;

// HACK: We have to construct the phantom field, but we don't have any value for it.
this.#__phantom = undefined as unknown as T;
}

/**
Expand Down

0 comments on commit 8facc3b

Please sign in to comment.