Mapping Types

Let's type safe the useQuery so that we parse the right result object state, which includes loading, error and data.

The auto-generated type definitions are available at src/generated/graphql.tsx file. Let's import GetMyTodosQuery relevant for this component.

import React, { Fragment, useState } from "react";
import { gql, useMutation } from "@apollo/client"
import TodoItem from "./TodoItem";
import TodoFilters from "./TodoFilters";
+ import {
+ GetMyTodosQuery,
+ } from '../../generated/graphql';

Now let's add this to useQuery:

const TodoPrivateList = () => {
const [filter, setFilter] = useState<string>("all");
- const { loading, error, data } = useQuery(GET_MY_TODOS);
+ const { loading, error, data } = useQuery<GetMyTodosQuery>(GET_MY_TODOS);
const filterResults = (filter: string): void => {
setFilter(filter);
};

We also have Todo type manually defined in the app boilerplate before the GraphQL integration. Let's import Todos type definition from the generated file.

import {
- GetMyTodosQuery
+ GetMyTodosQuery,
+ Todos
} from '../../generated/graphql';

Let's update the parts of code where the Todo type is being used.

let filteredTodos = data.todos;
if (filter === "active") {
- filteredTodos = data.todos.filter((todo: Todo) => todo.is_completed !== true);
+ filteredTodos = data.todos.filter((todo: Pick<Todos, "id" | "title" | "is_completed">) => todo.is_completed !== true);
} else if (filter === "completed") {
- filteredTodos = data.todos.filter((todo: Todo) => todo.is_completed === true);
+ filteredTodos = data.todos.filter((todo: Pick<Todos, "id" | "title" | "is_completed">) => todo.is_completed === true);
}
- const todoList = filteredTodos.map((todo: Todo, index: number) => (
+ const todoList = filteredTodos.map((todo: Pick<Todos, "id" | "title" | "is_completed">, index: number) => (
<TodoItem
key={'item'+index}
index={index}
todo={todo}
/>
));

We are using Pick<Todos, "id" | "title" | "is_completed">, instead of <Todos> to handle properties that are not used in the UI.

And now with updated todos, let's remove the manually declared type definition for todo.

- type Todo = {
- id: number,
- title: string,
- is_completed: boolean
- };
const TodoPrivateList = () => {
const [filter, setFilter] = useState<string>("all");
const { loading, error, data } = useQuery<GetMyTodosQuery>(GET_MY_TODOS);

Since we have updated the todo type here, the same needs to be reflected in TodoItem component as well.

Open src/components/Todo/TodoItem.tsx

import * as React from 'react';
+ import { Todos } from '../../generated/graphql';
- export type TodoItem = {
- id: number,
- title: string,
- is_completed: boolean
- };
interface TodoItemType {
index: number,
- todo: TodoItem
+ todo: Pick<Todos, "id" | "title" | "is_completed">;
};

Again let's update the TodoFilters component as well which imports the TodoItem type definition.

import * as React from 'react';
- import { TodoItem } from './TodoItem';
+ import { GetMyTodosQuery } from '../../generated/graphql';
interface filterResults {
(filter: string): void
}
interface TodoFiltersArgs {
- todos: TodoItem[],
+ todos: GetMyTodosQuery["todos"],
currentFilter: string,
filterResultsFn: filterResults,
clearCompletedFn: VoidFunction
}

Note that here we are importing GetMyTodosQuery and getting the type of todos property using GetMyTodosQuery["todos"]. Here todos is an array where as previously we were defining type for a single todo.

Did you find this page helpful?
Start with GraphQL on Hasura for Free
  • ArrowBuild apps and APIs 10x faster
  • ArrowBuilt-in authorization and caching
  • Arrow8x more performant than hand-rolled APIs
Promo
footer illustration
Brand logo
© 2024 Hasura Inc. All rights reserved
Github
Titter
Discord
Facebook
Instagram
Youtube
Linkedin
graphql-handbook