This course is no longer maintained and may be out-of-date. While it remains available for reference, its content may not reflect the latest updates, best practices, or supported features.

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
© 2025 Hasura Inc. All rights reserved
Github
Titter
Discord
Facebook
Instagram
Youtube
Linkedin
graphql-handbook