Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): Implement stricter variable typings on generic #2607

Merged
merged 9 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/stale-plants-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@urql/exchange-graphcache': major
'@urql/core': major
'@urql/preact': major
'urql': major
'@urql/svelte': major
'@urql/vue': major
---

Implement stricter variables types, which require variables to always be passed and match TypeScript types when the generic is set or inferred. This is a breaking change for TypeScript users potentially, unless all types are adhered to.
4 changes: 2 additions & 2 deletions exchanges/graphcache/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TypedDocumentNode } from '@urql/core';
import { AnyVariables, TypedDocumentNode } from '@urql/core';
import { GraphQLError, DocumentNode, FragmentDefinitionNode } from 'graphql';
import { IntrospectionData } from './ast';

Expand Down Expand Up @@ -211,7 +211,7 @@ export interface SerializedEntries {

export interface SerializedRequest {
query: string;
variables?: object;
variables: AnyVariables;
}

export interface StorageAdapter {
Expand Down
38 changes: 24 additions & 14 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { composeExchanges, defaultExchanges } from './exchanges';
import { fallbackExchange } from './exchanges/fallback';

import {
AnyVariables,
Exchange,
ExchangeInput,
GraphQLRequest,
Expand Down Expand Up @@ -92,52 +93,61 @@ export interface Client {
opts?: Partial<OperationContext> | undefined
): OperationContext;

createRequestOperation<Data = any, Variables = object>(
createRequestOperation<
Data = any,
Variables extends AnyVariables = AnyVariables
>(
kind: OperationType,
request: GraphQLRequest<Data, Variables>,
opts?: Partial<OperationContext> | undefined
): Operation<Data, Variables>;

/** Executes an Operation by sending it through the exchange pipeline It returns an observable that emits all related exchange results and keeps track of this observable's subscribers. A teardown signal will be emitted when no subscribers are listening anymore. */
executeRequestOperation<Data = any, Variables = object>(
executeRequestOperation<
Data = any,
Variables extends AnyVariables = AnyVariables
>(
operation: Operation<Data, Variables>
): Source<OperationResult<Data, Variables>>;

query<Data = any, Variables extends object = {}>(
query<Data = any, Variables extends AnyVariables = AnyVariables>(
query: DocumentNode | TypedDocumentNode<Data, Variables> | string,
variables?: Variables,
variables: Variables,
context?: Partial<OperationContext>
): PromisifiedSource<OperationResult<Data, Variables>>;

readQuery<Data = any, Variables extends object = {}>(
readQuery<Data = any, Variables extends AnyVariables = AnyVariables>(
query: DocumentNode | TypedDocumentNode<Data, Variables> | string,
variables?: Variables,
variables: Variables,
context?: Partial<OperationContext>
): OperationResult<Data, Variables> | null;

executeQuery<Data = any, Variables = object>(
executeQuery<Data = any, Variables extends AnyVariables = AnyVariables>(
query: GraphQLRequest<Data, Variables>,
opts?: Partial<OperationContext> | undefined
): Source<OperationResult<Data, Variables>>;

subscription<Data = any, Variables extends object = {}>(
subscription<Data = any, Variables extends AnyVariables = AnyVariables>(
query: DocumentNode | TypedDocumentNode<Data, Variables> | string,
variables?: Variables,
variables: Variables,
context?: Partial<OperationContext>
): Source<OperationResult<Data, Variables>>;

executeSubscription<Data = any, Variables = object>(
executeSubscription<
Data = any,
Variables extends AnyVariables = AnyVariables
>(
query: GraphQLRequest<Data, Variables>,
opts?: Partial<OperationContext> | undefined
): Source<OperationResult<Data, Variables>>;

mutation<Data = any, Variables extends object = {}>(
mutation<Data = any, Variables extends AnyVariables = AnyVariables>(
query: DocumentNode | TypedDocumentNode<Data, Variables> | string,
variables?: Variables,
variables: Variables,
context?: Partial<OperationContext>
): PromisifiedSource<OperationResult<Data, Variables>>;

executeMutation<Data = any, Variables = object>(
executeMutation<Data = any, Variables extends AnyVariables = AnyVariables>(
query: GraphQLRequest<Data, Variables>,
opts?: Partial<OperationContext> | undefined
): Source<OperationResult<Data, Variables>>;
Expand Down Expand Up @@ -310,7 +320,7 @@ export const Client: new (opts: ClientOptions) => Client = function Client(
return makeResultSource(operation);
}

return make(observer => {
return make<OperationResult>(observer => {
let source = active.get(operation.key);

if (!source) {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/exchanges/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface ObservableLike<T> {

export interface SubscriptionOperation {
query: string;
variables?: Record<string, unknown>;
variables: Record<string, unknown> | undefined;
key: string;
context: OperationContext;
}
Expand Down Expand Up @@ -70,7 +70,7 @@ export const subscriptionExchange = ({
const observableish = forwardSubscription({
key: operation.key.toString(36),
query: print(operation.query),
variables: operation.variables,
variables: operation.variables!,
context: { ...operation.context },
});

Expand Down
25 changes: 13 additions & 12 deletions packages/core/src/internal/fetchOptions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { DocumentNode, print } from 'graphql';

import { print } from 'graphql';
import { getOperationName, stringifyVariables } from '../utils';
import { Operation } from '../types';
import { AnyVariables, GraphQLRequest, Operation } from '../types';

export interface FetchBody {
query?: string;
Expand All @@ -14,15 +13,17 @@ const shouldUseGet = (operation: Operation): boolean => {
return operation.kind === 'query' && !!operation.context.preferGetMethod;
};

export const makeFetchBody = (request: {
query: DocumentNode;
variables?: object;
}): FetchBody => ({
query: print(request.query),
operationName: getOperationName(request.query),
variables: request.variables || undefined,
extensions: undefined,
});
export function makeFetchBody<
Data = any,
Variables extends AnyVariables = AnyVariables
>(request: Omit<GraphQLRequest<Data, Variables>, 'key'>): FetchBody {
return {
query: print(request.query),
operationName: getOperationName(request.query),
variables: request.variables || undefined,
extensions: undefined,
};
}

export const makeFetchURL = (
operation: Operation,
Expand Down
21 changes: 16 additions & 5 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@ export type RequestPolicy =
/** How the operation has */
export type CacheOutcome = 'miss' | 'partial' | 'hit';

/** A default type for variables */
export type AnyVariables = { [prop: string]: any } | void | undefined;

/** A Graphql query, mutation, or subscription. */
export interface GraphQLRequest<Data = any, Variables = object> {
export interface GraphQLRequest<
Data = any,
Variables extends AnyVariables = AnyVariables
> {
/** Unique identifier of the request. */
key: number;
query: DocumentNode | TypedDocumentNode<Data, Variables>;
variables?: Variables;
variables: Variables;
}

/** Metadata that is only available in development for devtools. */
Expand All @@ -70,14 +76,19 @@ export interface OperationContext {
}

/** A [query]{@link Query} or [mutation]{@link Mutation} with additional metadata for use during transmission. */
export interface Operation<Data = any, Variables = any>
extends GraphQLRequest<Data, Variables> {
export interface Operation<
Data = any,
Variables extends AnyVariables = AnyVariables
> extends GraphQLRequest<Data, Variables> {
readonly kind: OperationType;
context: OperationContext;
}

/** Resulting data from an [operation]{@link Operation}. */
export interface OperationResult<Data = any, Variables = any> {
export interface OperationResult<
Data = any,
Variables extends AnyVariables = AnyVariables
> {
/** The [operation]{@link Operation} which has been executed. */
operation: Operation<Data, Variables>;
/** The data returned from the Graphql server. */
Expand Down
11 changes: 7 additions & 4 deletions packages/core/src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {

import { hash, phash } from './hash';
import { stringifyVariables } from './stringifyVariables';
import { GraphQLRequest } from '../types';
import { AnyVariables, GraphQLRequest } from '../types';

interface WritableLocation {
loc: Location | undefined;
Expand Down Expand Up @@ -81,16 +81,19 @@ export const keyDocument = (q: string | DocumentNode): KeyedDocumentNode => {
return query as KeyedDocumentNode;
};

export const createRequest = <Data = any, Variables = object>(
export const createRequest = <
Data = any,
Variables extends AnyVariables = AnyVariables
>(
q: string | DocumentNode | TypedDocumentNode<Data, Variables>,
vars?: Variables
vars: Variables
): GraphQLRequest<Data, Variables> => {
if (!vars) vars = {} as Variables;
const query = keyDocument(q);
return {
key: phash(query.__key, stringifyVariables(vars)) >>> 0,
query,
variables: vars,
variables: vars as Variables,
};
};

Expand Down
14 changes: 10 additions & 4 deletions packages/preact-urql/src/components/Mutation.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { VNode } from 'preact';
import { DocumentNode } from 'graphql';
import {
AnyVariables,
TypedDocumentNode,
OperationResult,
OperationContext,
} from '@urql/core';
import { useMutation, UseMutationState } from '../hooks';

export interface MutationProps<Data = any, Variables = object> {
export interface MutationProps<
Data = any,
Variables extends AnyVariables = AnyVariables
> {
query: DocumentNode | TypedDocumentNode<Data, Variables> | string;
children: (arg: MutationState<Data, Variables>) => VNode<any>;
}

export interface MutationState<Data = any, Variables = object>
extends UseMutationState<Data, Variables> {
export interface MutationState<
Data = any,
Variables extends AnyVariables = AnyVariables
> extends UseMutationState<Data, Variables> {
executeMutation: (
variables?: Variables,
variables: Variables,
context?: Partial<OperationContext>
) => Promise<OperationResult<Data, Variables>>;
}
Expand Down
16 changes: 10 additions & 6 deletions packages/preact-urql/src/components/Query.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { VNode } from 'preact';
import { OperationContext } from '@urql/core';
import { AnyVariables, OperationContext } from '@urql/core';
import { useQuery, UseQueryArgs, UseQueryState } from '../hooks';

export interface QueryProps<Data = any, Variables = object>
extends UseQueryArgs<Variables, Data> {
export type QueryProps<
Data = any,
Variables extends AnyVariables = AnyVariables
> = UseQueryArgs<Variables, Data> & {
children: (arg: QueryState<Data, Variables>) => VNode<any>;
}
};

export interface QueryState<Data = any, Variables = object>
extends UseQueryState<Data, Variables> {
export interface QueryState<
Data = any,
Variables extends AnyVariables = AnyVariables
> extends UseQueryState<Data, Variables> {
executeQuery: (opts?: Partial<OperationContext>) => void;
}

Expand Down
24 changes: 14 additions & 10 deletions packages/preact-urql/src/components/Subscription.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VNode } from 'preact';
import { OperationContext } from '@urql/core';
import { AnyVariables, OperationContext } from '@urql/core';

import {
useSubscription,
Expand All @@ -8,23 +8,27 @@ import {
SubscriptionHandler,
} from '../hooks';

export interface SubscriptionProps<
export type SubscriptionProps<
Data = any,
Result = Data,
Variables = object
> extends UseSubscriptionArgs<Variables, Data> {
Variables extends AnyVariables = AnyVariables
> = UseSubscriptionArgs<Variables, Data> & {
handler?: SubscriptionHandler<Data, Result>;
children: (arg: SubscriptionState<Result, Variables>) => VNode<any>;
}
};

export interface SubscriptionState<Data = any, Variables = object>
extends UseSubscriptionState<Data, Variables> {
export interface SubscriptionState<
Data = any,
Variables extends AnyVariables = AnyVariables
> extends UseSubscriptionState<Data, Variables> {
executeSubscription: (opts?: Partial<OperationContext>) => void;
}

export function Subscription<Data = any, Result = Data, Variables = object>(
props: SubscriptionProps<Data, Result, Variables>
): VNode<any> {
export function Subscription<
Data = any,
Result = Data,
Variables extends AnyVariables = AnyVariables
>(props: SubscriptionProps<Data, Result, Variables>): VNode<any> {
const subscription = useSubscription<Data, Result, Variables>(
props,
props.handler
Expand Down
Loading