Content relationship types within another content relationship in a Slice

Hi @gabrielv,

This is a fairly complex problem that currently doesn't have an official solution. However, I wrote a helper that I think may help in your case.

It still requires manually listing out which fields you want typed at compile time and checked at run time, but it should reduce the amount of code you need to write.

// src/lib/isFilledRelatedData.ts

import {
  Content,
  FilledContentRelationshipField,
  LinkField,
  isFilled,
} from "@prismicio/client";

type DocumentData<TDocumentType extends Content.AllDocumentTypes["type"]> =
  Extract<Content.AllDocumentTypes, { type: TDocumentType }>["data"];

export function isFilledRelatedData<
  TDocumentType extends Content.AllDocumentTypes["type"],
  TFieldID extends keyof DocumentData<TDocumentType>,
>(
  linkField: LinkField,
  documentType: TDocumentType,
  fieldID: TFieldID,
): linkField is FilledContentRelationshipField & {
  data: {
    [P in keyof DocumentData<TDocumentType> as P extends TFieldID
      ? P
      : never]: DocumentData<TDocumentType>[P];
  };
} {
  return (
    isFilled.contentRelationship(linkField) &&
    linkField.type === documentType &&
    typeof linkField.data === "object" &&
    linkField.data !== null &&
    fieldID in linkField.data
  );
}

You can use it like this:

if (
  isFilledRelatedData(slice.primary.button_link, "page", "meta_title")
) {
  slice.primary.button_link.data.meta_title
  // ^ Typed as Content.PageDocumentData["meta_title"]
}

Or if you need to check and type nested related data:

if (
  isFilledRelatedData(slice.primary.button_link, "page", "parent") &&
  isFilledRelatedData(slice.primary.button_link.data.parent, "page", "meta_title" )
) {
  slice.primary.button_link.data.parent.data.meta_title
  // ^ Typed as Content.PageDocumentData["meta_title"]
}

Using this pattern, you can write your own helper functions specific to your project. How you build that will depend on your content models.

In the future, we may provide a helper like this in @prismicio/client. For now, you can copy this code into your project.