You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/source/development-testing/static-typing.mdx
+172Lines changed: 172 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -552,6 +552,178 @@ Variables are validated the same as `useQuery`. See [working with variables](#wo
552
552
553
553
Learn more about integrating TypeScript with data masking in the [data masking docs](../data/fragments#using-with-typescript).
554
554
555
+
## Overriding type implementations for built-in types
556
+
557
+
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.
558
+
559
+
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.
560
+
561
+
### Anatomy of HKTs
562
+
563
+
HKTs in Apollo Client consist of two parts:
564
+
565
+
- The HKT type definition - This provides the plumbing necessary to use your custom type implementation with Apollo Client
566
+
- The `TypeOverrides` interface - An interface used with [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) to provide the mapping for the overridden types to your HKT types
567
+
568
+
#### Creating an HKT type
569
+
570
+
You create HKT types by extending the `HKT` interface exported by `@apollo/client/utilities`.
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.
589
+
590
+
#### Mapping HKT types to its type override
591
+
592
+
Once your HKT type is created, you tell Apollo Client about it by using [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) using the `TypeOverrides` interface. This interface is included in Apollo Client to enable you to provide mappings to your custom type implementations.
593
+
594
+
Create a TypeScript file that defines the `TypeOverrides` interface for the `@apollo/client` module.
595
+
596
+
```ts title=apollo-client.d.ts
597
+
// This import is necessary to ensure all Apollo Client imports
598
+
// are still available to the rest of the application.
599
+
import"@apollo/client";
600
+
601
+
declaremodule"@apollo/client" {
602
+
exportinterfaceTypeOverrides {
603
+
TypeOverride1:MyTypeOverride;
604
+
}
605
+
}
606
+
```
607
+
608
+
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.
609
+
610
+
<Note>
611
+
612
+
`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](#available-type-overrides) for more information on which types can be overridden.
613
+
614
+
</Note>
615
+
616
+
### Example: Custom `dataState` types
617
+
618
+
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`](#type-narrowing-data-with-datastate) is set to specific values. The `Complete` type is used when `dataState` is `"complete"`, and `Streaming` is used when `dataState` is `"streaming"`.
619
+
620
+
For this example, we'll assume a custom type generation format where:
621
+
622
+
- 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.
623
+
624
+
```ts
625
+
typeStreamedQuery= {
626
+
// The streamed variant of the operation type is provided under the
627
+
// `__streaming` virtual property
628
+
__streaming?: {
629
+
user: { __typename:"User"; id:number } & (
630
+
| { name:string }
631
+
| { name?:never }
632
+
);
633
+
};
634
+
635
+
// The full result type includes all other fields in the type
This is a hypothetical format that doesn't exist in Apollo Client or any known code generation tool. This format is used specifically for this example to illustrate how to provide type overrides to Apollo Client.
651
+
652
+
</Note>
653
+
654
+
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.
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`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys) type.
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`.
671
+
672
+
Let's provide HKTs for our `Complete` and `Streaming` types. We'll put these in the same file as our type implementations.
And that's it! Now when `dataState` is `"complete"` or `"streaming"`, Apollo Client will use our custom type implementations 🎉.
712
+
713
+
### Available type overrides
714
+
715
+
The following utility types are available to override:
716
+
717
+
-`FragmentType<TFragmentData>` - Type used with fragments to ensure parent objects contain the fragment spread
718
+
-`Unmasked<TData>` - Unwraps masked types into the full result type
719
+
-`MaybeMasked<TData>` - Conditionally returns either masked or unmasked type
720
+
-`Complete<TData>` - Type returned when `dataState` is `"complete"`
721
+
-`Streaming<TData>` - Type returned when `dataState` is `"streaming"` (for `@defer` queries)
722
+
-`Partial<TData>` - Type returned when `dataState` is `"partial"`
723
+
-`AdditionalApolloLinkResultTypes` - Additional types that can be returned from Apollo Link operations
724
+
725
+
For more information about data masking types specifically, see the [data masking guide](../data/fragments#defining-your-own-masking-types-using-higher-kinded-types).
0 commit comments