From aeba7afbd7bbf687a21dc621d02f49d4c63863b0 Mon Sep 17 00:00:00 2001 From: Rose Koron Date: Thu, 17 Nov 2022 14:05:00 -0800 Subject: [PATCH] Further update the TypeGen article for style --- .../development-testing/static-typing.md | 347 +++++++++--------- 1 file changed, 171 insertions(+), 176 deletions(-) diff --git a/docs/source/development-testing/static-typing.md b/docs/source/development-testing/static-typing.md index e0a3c60af04..4ceeebd33b1 100644 --- a/docs/source/development-testing/static-typing.md +++ b/docs/source/development-testing/static-typing.md @@ -1,39 +1,81 @@ --- -title: Using Apollo with TypeScript +title: TypeScript with Apollo Client +descriptions: How to generate and use TS types in your application --- -As your application grows, you may find it helpful to include a type system to assist in development. Apollo supports type definitions for TypeScript out of the box. Apollo Client ships with definitions in its associated npm package, so installation should be done for you after the libraries are included in your project. +As your application grows, a type system can become an essential tool for catching bugs early and improving your overall developer experience. -These docs assume you already have TypeScript configured in your project, if not start [here](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide). +GraphQL uses a type system to clearly define the available data for each type and field in a GraphQL schema. Given that a GraphQL server's schema is strongly typed, we can generate TypeScript definitions automatically using a tool like [GraphQL Code Generator](https://www.the-guild.dev/graphql/codegen). We'll use our generated types to ensure type safety for the the _inputs_ and _results_ of our GraphQL operations. -The most common need when using type systems with GraphQL is to type the results of an operation. Given that a GraphQL server's schema is strongly typed, we can even generate TypeScript definitions automatically using a tool like [apollo-codegen](https://github.com/apollographql/apollo-codegen). In these docs however, we will be writing result types manually. +Below, we'll guide you through installing and configuring GraphQL Code Generator to generate types for your hooks and components. + +## Setting up your project + +> This article assumes your project already uses TypeScript, if not [begin by configuring your project to use TypeScript](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide) or [start a new project](https://create-react-app.dev/docs/adding-typescript/). + +To get started using GraphQL Code Generator, begin by installing the following packages (using Yarn or NPM): + +```bash +yarn add -D typescript @graphql-codegen/cli @graphql-codegen/client-preset +``` + +Next, we'll create a configuration file for GraphQL Code Generator, named [`codegen.ts`](https://www.the-guild.dev/graphql/codegen/docs/config-reference/codegen-config), at the root of our project: + +```ts title="codegen.ts" +import { CodegenConfig } from '@graphql-codegen/cli'; + +const config: CodegenConfig = { + schema: '', + documents: ['src/**/*.tsx'], + generates: { + './src/__generated__/': { + preset: 'client', + plugins: [], + presetConfig: { + gqlTagName: 'gql', + } + } + }, + ignoreNoDocuments: true, +}; + +export default config; +``` + +> There are multiple ways to [specify a schema](https://www.the-guild.dev/graphql/codegen/docs/config-reference/schema-field#root-level) in your `codegen.ts`, so pick the way that works best for your project setup. + +Finally, we'll add the following scripts to our `package.json` file: + +```json title="package.json" +{ + "scripts": { + "compile": "graphql-codegen", + "watch": "graphql-codegen -w", + } +} +``` + +Running either of the scripts above generates types based on the schema file or GraphQL API you provided in `codegen.ts`: + +```bash +$ yarn run compile +✔ Parse Configuration +✔ Generate outputs +``` ## Typing hooks -Apollo Client's `useQuery`, `useMutation` and `useSubscription` React hooks are fully typed, and Generics can be used to type both incoming operation variables and GraphQL result data. React Hook options and result types are listed in the [Hooks API](../api/react/hooks/) section of the docs. You can find a typed example of each Hook below. +GraphQL Code Generator automatically creates a `gql` function (from the `src/__genterated__/gql.ts` file), which we can use to type the variables going into, and results coming out of, of our React hooks. ### `useQuery` ```tsx import React from 'react'; -import { useQuery, gql } from '@apollo/client'; +import { useQuery } from '@apollo/client'; -interface RocketInventory { - id: number; - model: string; - year: number; - stock: number; -} - -interface RocketInventoryData { - rocketInventory: RocketInventory[]; -} +import { gql } from '../src/__generated__/gql'; -interface RocketInventoryVars { - year: number; -} - -const GET_ROCKET_INVENTORY = gql` +const GET_ROCKET_INVENTORY = gql(/* GraphQL */ ` query GetRocketInventory($year: Int!) { rocketInventory(year: $year) { id @@ -42,11 +84,13 @@ const GET_ROCKET_INVENTORY = gql` stock } } -`; +`); export function RocketInventoryList() { - const { loading, data } = useQuery( + // our query's result, data, is typed! + const { loading, data } = useQuery( GET_ROCKET_INVENTORY, + // variables are also typed! { variables: { year: 2019 } } ); return ( @@ -79,15 +123,17 @@ export function RocketInventoryList() { #### `fetchMore` and `subscribeToMore` -`useQuery` returns an instance of `QueryResult`. This includes the `fetchMore` and `subscribeToMore` functions. See the `Result` section of the [Queries](../data/queries#result) documentation page for detailed type information. Because these functions execute GraphQL operations, they accept type parameters. +The `useQuery` hook returns an instance of `QueryResult`, which includes the `fetchMore` and `subscribeToMore` functions. See [Queries for detailed type information](../data/queries#result). Because these functions execute GraphQL operations, they accept type parameters. -`fetchMore`'s type parameters are similar to those of `useQuery`. In fact, the type parameters are set to the same values as `useQuery`'s by default. Since both `fetchMore` and `useQuery` encapsulate a `query` operation, it's unlikely that you will need to pass any type arguments to `fetchMore`. Here's a sketch derived from the previous example: +By default, `fetchMore`'s type parameters are set to the same value of `useQuery`'s type parameters. Since both `fetchMore` and `useQuery` encapsulate a `query` operation, it's unlikely that you'll need to pass any type arguments to `fetchMore`. +Expanding our previous example, notice that we don't explicitly type `fetchMore`, because it defaults to using the same type parameters as `useQuery`: ```tsx // ... export function RocketInventoryList() { - const { fetchMore, loading, data } = useQuery( + const { fetchMore, loading, data } = useQuery( GET_ROCKET_INVENTORY, + // variables are typed! { variables: { year: 2019 } } ); @@ -95,14 +141,8 @@ export function RocketInventoryList() { //...