Search Result Typing| Hitchhikers Platform

Both Search UI React and Search Headless React are written fully in Typescript and provide intuitive typings for state, actions, and components. However, the library does not know ahead of time about the types of the entities in your Knowledge Graph.

Fortunately, the Yext CLI provides a command to generate Typescript types for your Search Experience based on the structure of your Knowledge Graph. You can then use these types in your project via Typescript Generics, so that you get type safety and type hints for displaying results.

Download the Yext CLI and log into your Yext Account to use the command.

Generate Types

The following command will generate a different TypeScript file for each vertical in a Search experience. Each file contains interfaces for all entity types configured in the respective vertical. By default all files will be created in a types folder.

yext types generate search [DESTINATION-DIR] [FLAG ...] [flags]

Flags:

  • --experienceKey string: The experience key of your search experience (Required)
  • -h, --help: Help for search

For example, if you navigated to the src folder in your project after running yext types generate search src/types --experienceKey doctor-search, your project structure will look something like this:

my-app
β”œβ”€β”€ src
β”‚   β”œβ”€β”€ types
β”‚   β”‚   β”œβ”€β”€ faqs.ts
β”‚   β”‚   └── doctors.ts
β”‚   β”œβ”€β”€ App.tsx
β”‚   β”œβ”€β”€ index.css
β”‚   β”œβ”€β”€ main.tsx
β”œβ”€β”€ .gitignore
β”œβ”€β”€ package-lock.json
└── package.json

Each file in types exports an interface for the entities returned by the vertical. For example:

export default interface Doctor {
  id: string;
  type: string;
  address: Address;
  specialty?: Specialty;
  firstName: string;
  gender: string;
  geocodedCoordinate: GeocodedCoordinate;
  headshot: Headshot;
  languages?: string[];
  lastName: string;
  name: string;
  uid: string;
}

export interface Address {
  city: string;
  line2: string;
  countryCode: string;
  region: string;
  line1: string;
  postalCode: string;
}

export interface Specialty {
  name: string;
}

export interface GeocodedCoordinate {
  longitude: number;
  latitude: number;
}

export interface Headshot {
  url: string;
  height: number;
  thumbnails?: Thumbnail[];
  width: number;
}

export interface Thumbnail {
  height: number;
  url: string;
  width: number;
}

Using Generated Types in Your Project

The generated Typescript types come in handy when using a custom CardComponent in conjunction with the <VerticalResults /> and <UniversalResults /> components. Each of these will provide type safety based on the shape of the Search API response.

<VerticalResults /> and <UniversalResults /> with Custom Cards

If you are using Search UI React and are using one of these two components you can pass the generated type directly to the component as a generic. For vertical results, it looks like this:

import { useSearchActions } from "@yext/search-headless-react";
import {
  SearchBar,
  VerticalResults,
  CardComponent,
  CardProps,
} from "@yext/search-ui-react";
import { useEffect } from "react";
import { Doctor } from "./types/doctors";

const DoctorCard = ({
  result,
}: CardProps<Doctor>): JSX.Element => {
  const product = result.rawData;
  return (
    <div>
      {/* Your Code Here */}
    </div>
  );
};

const App = (): JSX.Element => {
  const searchActions = useSearchActions();

  useEffect(() => {
    searchActions.setVertical("doctors");
  }, []);

  return (
    <div className="flex justify-center px-4 py-6">
      <div className="w-full max-w-5xl">
        <SearchBar />
        <VerticalResults<Doctor> CardComponent={DoctorCard} />
      </div>
    </div>
  );
}

export default App;

In this example, the DoctorCard is type safe.

If you are working with universal results, this works similarly, except that you provide a map from vertical keys to generated types, like so:

import {
  SearchBar,
  UniversalResults,
} from "@yext/search-ui-react";
import { useEffect } from "react";
import { Faq } from "./types/faqs";
import { Product } from "./types/products";

// declare or import DoctorCard and FaqCard

interface VerticalTypeMap {
  products: Product;
  faqs: Faq;
}

const App = (): JSX.Element => {
  return (
    <div className="flex justify-center px-4 py-6">
      <div className="w-full max-w-5xl">
        <SearchBar />
        <UniversalResults<VerticalTypeMap>
          verticalConfigMap={{
            products: { CardComponent: DoctorCard },
            faqs: { CardComponent: FaqCard },
          }}
        />
      </div>
    </div>
  );
};

export default App;

This guarantees type-safety for all cards passed to the verticalConfigMap.

Feedback