Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A helper to check if a content relationship's field was queried #330

Open
angeloashmore opened this issue Nov 29, 2023 · 1 comment
Open
Labels
enhancement New feature or request

Comments

@angeloashmore
Copy link
Member

angeloashmore commented Nov 29, 2023

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

@angeloashmore angeloashmore added the enhancement New feature or request label Nov 29, 2023
@mnzsss
Copy link

mnzsss commented May 8, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants