A helper to check if a content relationship's field was queried
Is your feature request related to a problem? Please describe.
Content from a content relationship field's document can be included in a query using the fetchLinks or graphQuery options. Typing those fields with TypeScript is difficult, however, requiring type assertions (as) or manual type parameters (getByUID<...>).
Describe the solution you'd like
A helper function could be provided that checks if a field is included in the query and automatically types the property. We can accomplish this using @prismicio/client's Content namespace.
Something like the following isFilledRelatedData could be used:
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
);
}
isFilledRelatedData()'s name can be misleading since it doesn't actually check if the field is filled. Instead, it checks that the linked field was queried via fetchLinks/graphQuery.
We would probably refocus the helper into something like hasRelationshipField(), which would still require you to check that the field was filled with isFilled().
if (
hasRelationshipField(item.brand, "brand", "title") &&
isFilled(item.brand)
) {
// ...
}
Describe alternatives you've considered
Project-specific runtime checkers can be written using isFilled, but it is cumbersome to write and maintain.
See https://community.prismic.io/t/types-for-content-relationship-in-a-slice/12067/2 for an example.
Additional context
- https://community.prismic.io/t/types-for-content-relationship-in-a-slice/12067
- https://community.prismic.io/t/content-relationship-types-within-another-content-relationship-in-a-slice/13866
I created this helper for case when I need return the typed relation data or undefined if is not filled.
import { Content, LinkField, isFilled } from '@prismicio/client'
type DocumentData<TDocumentType extends Content.AllDocumentTypes['type']> =
Extract<Content.AllDocumentTypes, { type: TDocumentType }>['data']
type RelatedData<T extends Content.AllDocumentTypes['type'], K> = {
[P in keyof DocumentData<T> as P]: DocumentData<T>[P]
}
export function isFilledRelatedData<
TDocumentType extends Content.AllDocumentTypes['type']
>({
linkField,
documentType
}: {
linkField?: LinkField
documentType: TDocumentType
}): RelatedData<TDocumentType> | undefined {
const isFilledRelation =
linkField &&
isFilled.contentRelationship(linkField) &&
linkField.type === documentType &&
typeof linkField.data === 'object' &&
linkField.data !== null
if (!isFilledRelation) return
return linkField.data as RelatedData<TDocumentType>
}
Example:
isFilledRelatedData({
linkField: content?.data.relation,
documentType: 'page'
})
In my case if use the recommended function above, I need verify if relation is loaded and return data in ternary check, like that:
const relation = content?.data.relation
? isFilledRelatedData(content?.data.relation, 'page', 'slug')
? content?.data.relation.data
: undefined
: undefined
This would be a great addition! Could the team please prioritize it @angeloashmore?