TypeScript with Apollo Client
How to use TypeScript in your application
As your application grows, a type system becomes an essential tool for catching bugs early and improving your overall developer experience.
GraphQL uses a type system to clearly define the available data for each type and field in a GraphQL schema. You can add type safety for your GraphQL operations using TypeScript.
This guide provides detail on how Apollo Client integrates TypeScript to provide you with type safety throughout your application.
Using operation types
By default, Apollo Client sets the type of the data
property to unknown
type when the data type cannot be determined. This provides a layer of safety to avoid accessing properties on data
that TypeScript doesn't know about.
The following example uses gql
to create a GraphQL document for a query. The query is typed as a DocumentNode
which doesn't provide type information about its data or variables.
1import { gql } from "@apollo/client";
2import { useQuery } from "@apollo/client/react";
3
4// This is of type `DocumentNode`
5const GET_ROCKET_INVENTORY = gql`
6 query GetRocketInventory {
7 rocketInventory {
8 id
9 model
10 year
11 stock
12 }
13 }
14`;
15
16function RocketInventory() {
17 const { data } = useQuery(GET_ROCKET_INVENTORY);
18 // ^? unknown
19
20 return (
21 <div>
22 {/* ❌ TypeScript Error: 'data' is of type 'unknown'. */}
23 {data.rocketInventory.map((rocket) => (
24 <Rocket key={rocket.id} rocket={rocket} />
25 ))}
26 </div>
27 );
28}
This however makes it difficult to work with the data
property. You either need to type cast each property on data
to suppress the error, or cast data
as an any
type (not recommended) which removes type safety entirely.
Instead, we can leverage the TypedDocumentNode
type to provide types for GraphQL documents. TypedDocumentNode
includes generic arguments for data and variables:
1import { TypedDocumentNode } from "@apollo/client";
2
3type QueryType = TypedDocumentNode<DataType, VariablesType>;
Apollo Client allows for the use of TypedDocumentNode
everywhere a DocumentNode
is accepted. This enables Apollo Client APIs to infer the data and variable types using the GraphQL document.
The following adds types to the query from the previous example using TypedDocumentNode
.
1import { useQuery, TypedDocumentNode } from "@apollo/client";
2
3// In a real application, consider generating types from your schema
4// instead of writing them by hand
5type GetRocketInventoryQuery = {
6 rocketInventory: {
7 __typename: "Rocket";
8 id: string;
9 model: string;
10 year: number;
11 stock: number;
12 };
13};
14
15type GetRocketInventoryQueryVariables = Record<string, never>;
16
17const GET_ROCKET_INVENTORY: TypedDocumentNode<
18 GetRocketInventoryQuery,
19 GetRocketInventoryQueryVariables
20> = gql`
21 query GetRocketInventory {
22 rocketInventory {
23 id
24 model
25 year
26 stock
27 }
28 }
29`;
30
31function RocketInventory() {
32 const { data } = useQuery(GET_ROCKET_INVENTORY);
33 // ^? GetRocketInventoryQuery | undefined
34
35 // checks for loading and error states are omitted for brevity
36
37 return (
38 <div>
39 {/* No more 'unknown' type error */}
40 {data.rocketInventory.map((rocket) => (
41 <Rocket key={rocket.id} rocket={rocket} />
42 ))}
43 </div>
44 );
45}
TypedDocumentNode
and relying on type inference throughout your application for your GraphQL operations instead of specifying the generic type arguments on Apollo Client APIs, such as useQuery
. This makes GraphQL documents more portable and provides better type safety wherever the document is used.Example
1// ❌ Don't leave GraphQL documents as plain `DocumentNode`s
2const query = gql`
3 # ...
4`;
5// ❌ Don't provide generic arguments to Apollo Client APIs
6const { data } = useQuery<QueryType, VariablesType>(query);
7
8// ✅ Add the type for the GraphQL document with `TypedDocumentNode`
9const query: TypedDocumentNode<QueryType, VariablesType> = gql`
10 # ...
11`;
12const { data } = useQuery(query);
TypedDocumentNode
, even when the GraphQL operation doesn't define any variables. This ensures you don't provide variable values to your query which results in runtime errors from your GraphQL server. Additionally, this ensures that TypeScript catches errors when you update your query over time and include required variables.If you use GraphQL Codegen, an empty variables type is generated for you using the type
Record<string, never>
.Type narrowing data
with dataState
Throughout the lifecycle of your component, the value of data
changes. data
is influenced by several factors that affect its value such as different options or the query's current state.
Some examples of options that influence the value of data
include:
returnPartialData
which might return partial data from the cacheerrorPolicy
which affects the value ofdata
when an error is returnedvariables
which affects when the query is re-executed as variables changefetchPolicy
which might provide a result from the cacheskip
which waits to execute the query
Some examples of the query state that influence the value of data
include:
The query is currently loading
The query successfully loads its result
The query returns partial data from the cache when
returnPartialData
istrue
The query returns an error
The query is currently streaming additional data while using
@defer
The combination of these states and options make it difficult to provide robust types for data
based solely on the loading
and error
properties.
Apollo Client provides a dataState
property for this purpose. dataState
provides information about the completeness of the data
property and includes type narrowing to give you better type safety without the need to add additional completeness checks.
The dataState
property has the following values:
"empty"
- No data is available.data
isundefined
"partial"
- Partial data is returned from the cache.data
isDeepPartial<TData>
"streaming"
- Data from a deferred query is incomplete and still streaming.data
isTData
"complete"
- Data fully satisfies the query.data
isTData
The following demonstrates how dataState
affects the type of data
.
returnPartialData: true
to demonstrate the type of data
when dataState
is partial
. When returnPartialData
is false
or omitted, dataState
does not include the partial
value and the type of data
does not include DeepPartial<TData>
.1const { data, dataState } = useQuery(GET_ROCKET_INVENTORY, {
2 returnPartialData: true,
3});
4
5data;
6// ^? GetRocketInventoryQuery | DeepPartial<GetRocketInventoryQuery> | undefined
7
8if (dataState === "empty") {
9 data;
10 // ^? undefined
11}
12
13if (dataState === "partial") {
14 data;
15 // ^? DeepPartial<GetRocketInventoryQuery>
16}
17
18if (dataState === "streaming") {
19 data;
20 // ^? GetRocketInventoryQuery
21}
22
23if (dataState === "complete") {
24 data;
25 // ^? GetRocketInventoryQuery
26}
data
is the same when dataState
is either streaming
or complete
. Additionally, dataState
always includes streaming
as a value, even when @defer
is not used and isn't seen at runtime. This is because it is difficult to determine whether a query uses @defer
based solely on the output format of the query type.If you use a type format that makes this possible, you can provide your own type implementations for the
complete
and streaming
states to provide a corrected type. See the guide on overriding types to learn how to provide your own type implementations.Working with variables
When your GraphQL operations include variables, TypeScript ensures you provide all required variables with the correct types. Additionally, TypeScript ensures you omit variables that aren't included in the operation.
The following adds a non-null variable to the GetRocketInventory
query used in previous examples.
1const GET_ROCKET_INVENTORY: TypedDocumentNode<
2 GetRocketInventoryQuery,
3 GetRocketInventoryQueryVariables
4> = gql`
5 query GetRocketInventory($year: Int!) {
6 rocketInventory(year: $year) {
7 id
8 model
9 year
10 stock
11 }
12 }
13`;
14
15function RocketInventory() {
16 // ❌ TypeScript Error: Expected 2 arguments, but got 1.
17 const { data } = useQuery(GET_ROCKET_INVENTORY);
18
19 // ❌ TypeScript Error: Property 'variables' is missing in type '{}'
20 const { data } = useQuery(GET_ROCKET_INVENTORY, {});
21
22 // ❌ TypeScript Error: Property 'year' is missing in type '{}'
23 const { data } = useQuery(GET_ROCKET_INVENTORY, { variables: {} });
24
25 // ✅ Correct: Required variable provided
26 const { data } = useQuery(GET_ROCKET_INVENTORY, {
27 variables: { year: 2024 },
28 });
29
30 // ❌ TypeScript Error: Type 'string' is not assignable to type 'number'
31 const { data } = useQuery(GET_ROCKET_INVENTORY, {
32 variables: { year: "2024" },
33 });
34
35 // ❌ TypeScript Error: 'notAVariable' does not exist in type '{ id: string }'
36 const { data } = useQuery(GET_ROCKET_INVENTORY, {
37 variables: { year: "2024", notAVariable: true },
38 });
39}
For operations with optional variables, TypeScript allows you to omit them:
1const GET_ROCKET_INVENTORY: TypedDocumentNode<
2 GetRocketInventoryQuery,
3 GetRocketInventoryQueryVariables
4> = gql`
5 query GetRocketInventory($model: String, $year: Int) {
6 rocketInventory(model: $model, year: $year) {
7 id
8 model
9 }
10 }
11`;
12
13function RocketInventory() {
14 // ✅ All valid - all variables are optional
15 const { data } = useQuery(GET_ROCKET_INVENTORY);
16
17 // ✅ All valid - All variables satisfy the variables type
18 const { data } = useQuery(GET_ROCKET_INVENTORY, {
19 variables: { model: "Falcon" },
20 });
21
22 // ✅ All valid - All variables satisfy the variables type
23 const { data } = useQuery(GET_ROCKET_INVENTORY, {
24 variables: { year: 2024 },
25 });
26
27 // ✅ All valid - All variables satisfy the variables type
28 const { data } = useQuery(GET_ROCKET_INVENTORY, {
29 variables: { model: "Falcon", year: 2024 },
30 });
31
32 // ❌ TypeScript Error: 'notAVariable' does not exist in type '{ id: string }'
33 const { data } = useQuery(GET_ROCKET_INVENTORY, {
34 variables: { model: "Falcon", year: 2024, notAVariable: true },
35 });
36}
Mutations
For a more comprehensive guide on using mutations, see the Mutations guide.
Like useQuery
, you provide useMutation
a TypedDocumentNode
. This adds the query type for the data
property and ensures that variables
are validated by TypeScript.
1import { gql, TypedDocumentNode } from "@apollo/client";
2import { useMutation } from "@apollo/client/react";
3
4// In a real application, consider generating types from your schema
5// instead of writing them by hand
6type SaveRocketMutation = {
7 saveRocket: {
8 model: string;
9 } | null;
10};
11
12type SaveRocketMutationVariables = {
13 rocket: {
14 model: string;
15 year: number;
16 stock: number;
17 };
18};
19
20const SAVE_ROCKET: TypedDocumentNode<
21 SaveRocketMutation,
22 SaveRocketMutationVariables
23> = gql`
24 mutation SaveRocket($rocket: RocketInput!) {
25 saveRocket(rocket: $rocket) {
26 model
27 }
28 }
29`;
30
31export function NewRocketForm() {
32 const [model, setModel] = useState("");
33 const [year, setYear] = useState(0);
34 const [stock, setStock] = useState(0);
35
36 const [addRocket, { data }] = useMutation(SAVE_ROCKET, {
37 // ^? SaveRocketMutation | null | undefined
38 variables: { rocket: { model, year, stock } },
39 });
40
41 return (
42 <form>
43 <p>
44 <label>Model</label>
45 <input name="model" onChange={(e) => setModel(e.target.value)} />
46 </p>
47 <p>
48 <label>Year</label>
49 <input
50 type="number"
51 name="year"
52 onChange={(e) => setYear(+e.target.value)}
53 />
54 </p>
55 <p>
56 <label>Stock</label>
57 <input
58 type="number"
59 name="stock"
60 onChange={(e) => setStock(e.target.value)}
61 />
62 </p>
63 <button onClick={() => addRocket()}>Add rocket</button>
64 </form>
65 );
66}
useQuery
, useMutation
doesn't provide a dataState
property. data
isn't tracked the same way as useQuery
because its value is not read from the cache. The value of data
is a set result of the last execution of the mutation.Using variables with useMutation
useMutation
allows you to provide variables to either the hook or the mutate function returned in the result tuple. When variables are provided to both the hook and mutate function, they are shallowly merged (see the Mutations guide for more information).
This behavior affects how TypeScript checks required variables with useMutation
. Required variables must be provided to either the hook or the mutate function. If a required variable is not provided to the hook, the mutate function must include it. Required variables provided to the hook make them optional in the mutate function.
The following uses the previous example but flattens the variable declarations to demonstrate how required variables affect TypeScript validation.
1const SAVE_ROCKET: TypedDocumentNode<
2 SaveRocketMutation,
3 SaveRocketMutationVariables
4> = gql`
5 mutation SaveRocket($model: String!, $year: Int!, $stock: Int) {
6 saveRocket(rocket: { model: $model, year: $year, stock: $stock }) {
7 model
8 }
9 }
10`;
11
12export function NewRocketForm() {
13 // No required variables provided to the hook
14 const [addRocket] = useMutation(SAVE_ROCKET);
15
16 // ❌ TypeScript Error: Expected 1 argument, but got 0.
17 addRocket();
18 // ❌ TypeScript Error: Property 'variables' is missing in type '{}'
19 addRocket({});
20 // ❌ TypeScript Error: Type '{}' is missing the following properties from '{ year: number; model: string;, stock?: number }': model, year
21 addRocket({ variables: {} });
22 // ❌ TypeScript Error: Property 'year' is missing in type '{ model: string }'
23 addRocket({ variables: { model: "Falcon" } });
24 // ✅ Correct: All required variables provided
25 addRocket({ variables: { model: "Falcon", year: 2025 } });
26 // ❌ TypeScript Error: 'notAVariable' does not exist in type '{ year: number; model: string;, stock?: number }'
27 addRocket({ variables: { model: "Falcon", year: 2025, notAVariable: true } });
28
29 // Some required variables provided to the hook
30 const [addRocket] = useMutation(SAVE_ROCKET, {
31 variables: { model: "Falcon" },
32 });
33
34 // ❌ TypeScript Error: Expected 1 argument, but got 0.
35 addRocket();
36 // ❌ TypeScript Error: Property 'variables' is missing in type '{}'
37 addRocket({});
38 // ❌ TypeScript Error: Property 'year' is missing in type '{}'
39 addRocket({ variables: {} });
40 // ✅ Correct: All remaining required variables provided
41 addRocket({ variables: { year: 2025 } });
42 // ❌ TypeScript Error: 'notAVariable' does not exist in type '{ year: number; model?: string;, stock?: number }'
43 addRocket({ variables: { year: 2025, notAVariable: true } });
44
45 // All required variables provided to the hook
46 const [addRocket] = useMutation(SAVE_ROCKET, {
47 variables: { model: "Falcon", year: 2025 },
48 });
49
50 // ✅ Correct: All required variables are provided to the hook
51 addRocket();
52 // ✅ Correct: All required variables are provided to the hook
53 addRocket({ variables: { stock: 10 } });
54 // ❌ TypeScript Error: 'notAVariable' does not exist in type '{ year?: number; model?: string;, stock?: number }'
55 addRocket({ variables: { notAVariable: true } });
56}
Subscriptions
For a more comprehensive guide on using subscriptions, see the Subscriptions guide.
Like useQuery
, you provide useSubscription
a TypedDocumentNode
. This adds the query type for the data
property and ensures that variables
are validated by TypeScript.
1import { gql, TypedDocumentNode } from "@apollo/client";
2import { useSubscription } from "@apollo/client/react";
3
4// In a real application, consider generating types from your schema
5// instead of writing them by hand
6type GetLatestNewsSubscription = {
7 latestNews: {
8 content: string;
9 };
10};
11
12type GetLatestNewsSubscriptionVariables = Record<string, never>;
13
14const LATEST_NEWS: TypedDocumentNode<
15 GetLatestNewsSubscription,
16 GetLatestNewsSubscriptionVariables
17> = gql`
18 subscription GetLatestNews {
19 latestNews {
20 content
21 }
22 }
23`;
24
25export function LatestNews() {
26 const { data } = useSubscription(LATEST_NEWS);
27 // ^? GetLatestNewsSubscription | undefined
28
29 return (
30 <div>
31 <h5>Latest News</h5>
32 <p>{data?.latestNews.content}</p>
33 </div>
34 );
35}
Variables are validated the same as useQuery
. See working with variables to learn more about how required variables are validated.
Defining context types
Apollo Client enables you to pass custom context through to the link chain. By default, context is typed as Record<string, any>
to allow for any arbitrary value as context. However, this default has a few downsides. You don't get proper type safety and you don't get autocomplete suggestions in your editor to understand what context options are available to you.
You can define your own context types for better type safety in Apollo Client using TypeScript's declaration merging.
Adding custom context properties
To define types for your custom context properties, create a TypeScript file and define the DefaultContext
interface.
1// This import is necessary to ensure all Apollo Client imports
2// are still available to the rest of the application.
3import "@apollo/client";
4
5declare module "@apollo/client" {
6 interface DefaultContext {
7 myProperty?: string;
8 requestId?: number;
9 }
10}
Now when you pass context for operations, TypeScript will validate the types:
1const { data } = useQuery(MY_QUERY, {
2 context: {
3 myProperty: "value", // ✅ Valid value
4 requestId: "123", // ❌ Type 'string' is not assignable to type 'number | undefined'
5 },
6});
SetContextLink
or a custom link first. Reserve required properties for options that are truly meant to be provided to every hook or request to your server and cannot be provided by a link in your link chain.context
option is provided when it contains required properties like it does with the variables
option. However, adding the context
option might result in a TypeScript error when any required properties are not provided.Using context from built-in links
Some links provided by Apollo Client have built-in context option types. You can extend DefaultContext
with these types to get proper type checking for link-specific options.
The following example adds HttpLink
's context types to DefaultContext
:
1import "@apollo/client";
2import { HttpLink } from "@apollo/client";
3
4declare module "@apollo/client" {
5 interface DefaultContext extends HttpLink.ContextOptions {}
6}
Now when you pass context for operations, TypeScript will validate the types:
1const { data } = useQuery(MY_QUERY, {
2 context: {
3 headers: {
4 "X-Custom-Header": "value", // ✅ Valid header value
5 },
6 credentials: "include", // ✅ Valid RequestCredentials value
7 fetchOptions: {
8 mode: "none", // ❌ Type "none" is not assignable to type 'RequestMode | undefined'
9 },
10 },
11});
If you are using more than one link that has context options, you can extend from each link's context options:
1import "@apollo/client";
2import type { BaseHttpLink } from "@apollo/client/link/http";
3import type { ClientAwarenessLink } from "@apollo/client/link/client-awareness";
4
5declare module "@apollo/client" {
6 interface DefaultContext
7 extends BaseHttpLink.ContextOptions,
8 ClientAwarenessLink.ContextOptions {}
9}
The following built-in links provide context option types:
Using context in custom links
With context types defined, context properties accessed in custom links are properly type checked when reading or writing context.
1import { ApolloLink } from "@apollo/client";
2
3const customLink = new ApolloLink((operation, forward) => {
4 const context = operation.getContext();
5
6 // TypeScript knows about your custom properties
7 console.log(context.myProperty); // string | undefined
8 console.log(context.requestId); // number | undefined
9
10 operation.setContext({
11 requestId: "123", // ❌ Type 'string' is not assignable to type 'number | undefined'
12 });
13
14 return forward(operation);
15});
Data masking
Learn more about integrating TypeScript with data masking in the data masking docs.
@defer
Learn more about integrating TypeScript with incremental responses the @defer
docs.
Overriding type implementations for built-in types
Apollo Client makes it possible to use custom implementations of certain built-in utility types. This enables you to work with custom type outputs that might otherwise be incompatible with the default type implementations in Apollo Client.
You use a technique called higher-kinded types (HKT) to provide your own type implementations for Apollo Client's utility types. You can think of higher-kinded types as a way to define types and interfaces with generics that can be filled in by Apollo Client internals at a later time. Passing around un-evaluated types is otherwise not possible in TypeScript.
Anatomy of HKTs
HKTs in Apollo Client consist of two parts:
The HKT type definition - This provides the plumbing necessary to use your custom type implementation with Apollo Client
The
TypeOverrides
interface - An interface used with declaration merging to provide the mapping for the overridden types to your HKT types
Creating an HKT type
You create HKT types by extending the HKT
interface exported by @apollo/client/utilities
.
1import { HKT } from "@apollo/client/utilities";
2
3// The implementation of the type
4type MyCustomImplementation<GenericArg1, GenericArg2> = SomeOtherUtility<
5 GenericArg1,
6 GenericArg2
7>;
8
9interface MyTypeOverride extends HKT {
10 arg1: unknown; // GenericArg1
11 arg2: unknown; // GenericArg2
12 return: MyCustomImplementation<this["arg1"], this["arg2"]>;
13}
You can think of each property on the HKT
type as a placeholder. Each arg*
property corresponds to a generic argument used for the implementation. The return
property provides the mapping to the actual implementation of the type, using the arg*
values as generic arguments.
Mapping HKT types to its type override
Once your HKT type is created, you tell Apollo Client about it by using declaration merging using the TypeOverrides
interface. This interface is included in Apollo Client to enable you to provide mappings to your custom type implementations.
Create a TypeScript file that defines the TypeOverrides
interface for the @apollo/client
module.
1// This import is necessary to ensure all Apollo Client imports
2// are still available to the rest of the application.
3import "@apollo/client";
4
5declare module "@apollo/client" {
6 export interface TypeOverrides {
7 TypeOverride1: MyTypeOverride;
8 }
9}
Each key in the TypeOverrides
interface corresponds to an overridable type in Apollo Client and its value maps to an HKT type that provides the definition for that type.
TypeOverride1
is used as an example in the previous code block but is not a valid overridable type so it is ignored. See the available type overrides for more information on which types can be overridden.Example: Custom dataState
types
Let's add our own type overrides for the Complete
and Streaming
utility types. These types are used to provide types for the data
property when dataState
is set to specific values. The Complete
type is used when dataState
is "complete"
, and Streaming
is used when dataState
is "streaming"
.
For this example, we'll assume a custom type generation format where:
Streamed types (i.e. operation types that use the
@defer
directive) include a__streaming
virtual property. Its value is the operation type that should be used when the result is still streaming from the server.TypeScript1type StreamedQuery = { 2 // The streamed variant of the operation type is provided under the 3 // `__streaming` virtual property 4 __streaming?: { 5 user: { __typename: "User"; id: number } & ( 6 | { name: string } 7 | { name?: never } 8 ); 9 }; 10 11 // The full result type includes all other fields in the type 12 user: { __typename: "User"; id: number; name: string }; 13};
Complete types which provide the full type of a query
TypeScript1type CompleteQuery = { 2 user: { __typename: "User"; id: number; name: string }; 3};
First, let's define our custom implementation of the Streaming
type. The implementation works by checking if the __streaming
virtual property exists on the type. If so, it returns the value on the __streaming
property as the type, otherwise it returns the input type unmodified.
1type Streaming<TData> =
2 TData extends { __streaming?: infer TStreamingData } ? TStreamingData : TData;
Now let's define our custom implementation of the Complete
type. The implementation works by removing the __streaming
virtual property on the input type. This can be accomplished using the built-in Omit
type.
1type Streaming<TData> =
2 TData extends { __streaming?: infer TStreamingData } ? TStreamingData : TData;
3
4type Complete<TData> = Omit<TData, "__streaming">;
Now we need to define higher-kinded types for each of these implementations. This provides the bridge needed by Apollo Client to use our custom type implementations. This is done by extending the HKT
interface exported by @apollo/client/utilities
.
Let's provide HKTs for our Complete
and Streaming
types. We'll put these in the same file as our type implementations.
1import { HKT } from "@apollo/client/utilities";
2
3type Streaming<TData> =
4 TData extends { __streaming?: infer TStreamingData } ? TStreamingData : TData;
5
6type Complete<TData> = Omit<TData, "__streaming">;
7
8export interface StreamingHKT extends HKT {
9 arg1: unknown; // TData
10 return: Streaming<this["arg1"]>;
11}
12
13export interface CompleteHKT extends HKT {
14 arg1: unknown; // TData
15 return: Complete<this["arg1"]>;
16}
With our HKT types in place, we now need to tell Apollo Client about them. We'll need to provide our type overrides on the TypeOverrides
interface.
Create a TypeScript file and define a TypeOverrides
interface for the @apollo/client
module.
1// This import is necessary to ensure all Apollo Client imports
2// are still available to the rest of the application.
3import "@apollo/client";
4import { CompleteHKT, StreamingHKT } from "./custom-types";
5
6declare module "@apollo/client" {
7 export interface TypeOverrides {
8 Complete: CompleteHKT;
9 Streaming: StreamingHKT;
10 }
11}
And that's it! Now when dataState
is "complete"
or "streaming"
, Apollo Client will use our custom type implementations 🎉.
Available type overrides
The following utility types are available to override:
FragmentType<TFragmentData>
- Type used with fragments to ensure parent objects contain the fragment spreadUnmasked<TData>
- Unwraps masked types into the full result typeMaybeMasked<TData>
- Conditionally returns either masked or unmasked typeComplete<TData>
- Type returned whendataState
is"complete"
Streaming<TData>
- Type returned whendataState
is"streaming"
(for@defer
queries)Partial<TData>
- Type returned whendataState
is"partial"
AdditionalApolloLinkResultTypes<TData, TExtensions>
- Additional types that can be returned from Apollo Link operations
For more information about data masking types specifically, see the data masking guide.