Hi Joseph,
Typing a deeply nested Content Relationship field with a data
property is challenging if you do it from the top-level client query, which is shown in the documentation's example (see here).
If you need to type a Slice's Content Relationship field, I recommend doing runtime checking with type predicates. This saves you from performing complicated TypeScript gymnastics to override the Slice's types, which can contain many branches (think: different Slice types, each with their own variations, and each with primary and items fields).
A Content Relationship's data
field is typed as unknown
by default, which works well with type predicates.
The following example checks that a Content Relationship field contains a data
property with a parent
field. Remember that the data
property is only filled if the top-level query included a fetchLinks
or graphQuery
option, which may not always be the case.
import { Content } from "@prismicio/client";
import { PrismicText, SliceComponentProps } from "@prismicio/react";
import * as prismicH from "@prismicio/helpers";
import * as prismicT from "@prismicio/types";
const hasParentData = <
TContentRelationshipField extends prismicT.ContentRelationshipField
>(
contentRelationshipField: TContentRelationshipField
): contentRelationshipField is TContentRelationshipField & {
data: {
parent: Pick<Content.PageDocument["data"], "title">;
};
} => {
return (
prismicH.isFilled.contentRelationship(contentRelationshipField) &&
typeof contentRelationshipField.data === "object" &&
contentRelationshipField.data !== null &&
"parent" in contentRelationshipField.data
);
};
export default function Image({
slice,
}: SliceComponentProps<Content.ImageSlice>) {
return (
<div>
{hasParentData(slice.primary.link) && (
<PrismicText field={slice.primary.link.data.parent.title} />
)}
</div>
);
}
The hasParentData
function checks that the provided field contains a data
property, is an object, and contains a parent
field. It types the data
property by adding a data.parent
property to the provided field (see the is
keyword in the function's return type).
If you check this code in your editor, you'll see that slice.primary.link.data.parent.title
is typed as PageDocument['data']['title']
(in my case, that is a Rich Text field). You will need to adjust the type names according to your project.
This pattern can be used across your app whenever you need to check if a Content Relationship field has linked fields.
If you have any questions, let me know!