Hello, Prismic People !
Some time ago, I encountered issues with Content Relationship types in a Slice. However, I managed to resolve them by following the solution provided by angelsashmore, which I found in the responses to the topic created by Joseph.
Topic:
Types for content relationship in a Slice
And also in the documentation:
Use TypeScript with Next.js
Upon exploring the solution further, I managed to condense the code and arrive at the following:
import * as prismicH from "@prismicio/helpers";
import * as prismicT from "@prismicio/types";
const hasServiceData = <
TContentRelationshipField extends prismicT.ContentRelationshipField
>(
contentRelationshipField: TContentRelationshipField
): contentRelationshipField is TContentRelationshipField &
Content.ServiceDocument => {
return (
prismicH.isFilled.contentRelationship(contentRelationshipField) &&
typeof contentRelationshipField.data === "object" &&
contentRelationshipField.data !== null &&
contentRelationshipField.id !== null &&
contentRelationshipField.uid !== null
);
};
// Example of usage within a Slice function
hasServiceData(slice.items[0].service) &&
console.log(
slice.items[0].service.data.title,
slice.items[0].service.id,
slice.items[0].service.uid,
slice.items[0].service.data.testimonials[0]?.author
);
"If everything went well, then what's the problem ?"
The problem is that you don't have just one Content Relationship, but a Content Relationship (case_study) within another Content Relationship (service).
So the current solution works for all the other fields, but when it gets to the innermost Content Relationship, it stops there and doesn't do the typing.
I tried to change everything to see what could be done, but I only found two ways to solve this, the first would be to create another function just for this internal Content Relationship, and the second would be to do it more "by hand", that is, uid: uid , id: id and so on until you arrive.
The first solution is acceptable, but I would like to keep everything in just one function, and the second solution I didn't find very interesting, as it would be a more "manual" process, something that would get even worse the more and more fields were added.
The code for the second solution would look something like this:
const hasServiceData = <
TContentRelationshipField extends prismicT.ContentRelationshipField
>(
contentRelationshipField: TContentRelationshipField
): contentRelationshipField is TContentRelationshipField & {
id: Content.ServiceDocument["id"];
uid: Content.ServiceDocument["uid"];
data: {
title: Content.ServiceDocumentData["title"];
header_image: Content.ServiceDocumentData["header_image"];
tag: Content.ServiceDocumentData["tag"];
color: Content.ServiceDocumentData["color"];
description: Content.ServiceDocumentData["description"];
headline: Content.ServiceDocumentData["headline"];
testimonials: Content.ServiceDocumentData["testimonials"];
highlights: Content.ServiceDocumentData["highlights"];
case_studies: { case_study: Content.CaseStudyDocument }[];
};
} => {
return (
prismicH.isFilled.contentRelationship(contentRelationshipField) &&
typeof contentRelationshipField.data === "object" &&
contentRelationshipField.data !== null &&
contentRelationshipField.id !== null &&
contentRelationshipField.uid !== null
);
};
So, do you guys have any ideas for a third solution that is more 'elegant'? Or do you think that the two proposals mentioned above are the only ways to infer types from a Content Relationship nested within another Content Relationship in a Slice?