Generating types for GROQ query results
With the TypeGen configuration set up to look for GROQ queries in your front end project, the last step is to make those queries discoverable for the tooling.
First, go into your front end folder. If you follow this course with the code from the Day One course, you can search for EVENTS_QUERY to find the index route file.
const EVENTS_QUERY = `*[_type == "event" && defined(slug.current)]{_id, name, slug, date}|order(date desc)`;To get types for GROQ query results from TypeGen, you need to make sure of the following:
- The string with the query is assigned to a variable
- The variable name needs to be globally unique since it’s used for the type name
- The string needs to be a syntactically valid GROQ query (for example, you should be able to run it successfully in the Vision plugin or on groq.dev)
- The string with the query needs to be prefixed with the
groqtemplate literal or thedefineQueryhelper function
defineQuery function over groq, as it also unlocks automatic type inference when using Sanity ClientThe EVENTS_QUERY is only missing the last point to be picked up by the TypeGen command.
If you’re using Next.js, you can import groq and defineQuery from the next-sanity package.
For other frameworks, you can install the groq package:
npm install groqAdd the defineQuery function like this; remember to save the file after:
import { defineQuery } from 'next-sanity'// import { defineQuery } from 'groq' // in other frameworks
const EVENTS_QUERY = defineQuery(`*[_type == "event" && defined(slug.current)]{_id, name, slug, date}|order(date desc)`)defineQuery into the route file with the EVENTS_QUERY variabledefineQuery template literal to your GROQ query stringBoth the groq template literal and defineQuery function will add syntax highlighting to VS Code when you have the Sanity extension installed. It’s also used by the TypeGen tooling to identify something as a GROQ query.
Return to the Studio folders and run the npx sanity typegen generate command again. It should pick up the GROQ query, and the output should be like this:
✔ Generated TypeScript types for 14 schema types and 1 GROQ queries in 1 files into: ../day-one-with-sanity-nextjs/src/sanity/types.tsOpen your types.ts file in your front end folder, and search for EVENTS_QUERY to find your new type:
// Source: ../frontend/src/app/page.tsx// Variable: EVENTS_QUERY// Query: *[_type == "event" && defined(slug.current)]{_id, name, slug, date}|order(date desc)export type EVENTS_QUERY_RESULT = Array<{ _id: string; name: string | null; slug: Slug | null; date: string | null;}>;EVENTS_QUERY_RESULT type in types.tsNow that you have generated types for your schema and GROQ queries, TypeGen provides powerful utilities to work with these types in real-world scenarios. Two utilities in particular, Get and FilterByType, make it much easier to extract and work with nested types from your Sanity documents.
The Get utility extracts deeply nested properties from your types—up to 20 levels deep—without complex TypeScript gymnastics. This is especially useful when working with nested objects, references, or array items in your schema.
Instead of writing verbose type expressions with multiple NonNullable wrappers, you can use a simple path syntax:
import type { Get } from 'sanity'import type { Event } from './sanity/types'
// Extract a deeply nested typetype VenueCity = Get<Event, 'venue.city'>
// Works with arrays tootype FirstImageAlt = Get<Event, 'images[0].alt'>
// Compare to the old way:type OldWay = NonNullable<NonNullable<Event['venue']>['city']>The Get utility handles nullable types automatically and provides a clean, readable syntax that mirrors how you'd access the property in JavaScript.
The FilterByType utility filters specific types from unions using the _type discriminator. This is particularly useful when working with Portable Text or modular content arrays where you have a union of different block types.
For example, if you have a page builder field that can contain multiple block types, you can easily extract just the types you need:
import type { FilterByType } from 'sanity'import type { PageBuilder } from './sanity/types'
// Extract only hero blocks from a union of block typestype HeroBlock = FilterByType<PageBuilder, 'hero'>
// Works with multiple types tootype ContentBlocks = FilterByType<PageBuilder, 'hero' | 'textBlock' | 'imageGallery'>This makes it easy to create type-safe component props or helper functions that work with specific block types from your modular content.
Thanks to automatic type inference, this type should be automatically applied when using Sanity Client's fetch.
const events = await client.fetch(EVENTS_QUERY); // ^ typed as EVENTS_QUERY_RESULTUsing these GROQ query result types with Sanity Client is covered in the Display content in Next.js lesson of the Day one content operations course, which you should have already completed.