How do I typing Document with fetchLinks?

Hi community,

Is there a good way to type definition the fetchLinks information?

I think the project retrieved as follows would have access to project.data.creator.data.name.

// getStaticProps()
const project = await client.getByUID<ProjectDocument>('project', uid, {
fetchLinks: ['creator.name', 'creator.face']
})

However, in props, the fetchLinks information is lost because it is only typed ProjectDocument<string>.

type ProjectProps = {
  project: ProjectDocument<string>
}

const Project: NextPage<ProjectProps> = ({ project }) => {
  const name = project.data.creator.data.name //Property 'data' does not exist on type 'EmptyLinkField<"Document"> | FilledLinkToDocumentField<"creator", string, never>'.
                                              // Property 'data' does not exist on type 'EmptyLinkField<"Document">'.ts(2339)
  return <>...</>
}

How do I keep the fetchLinks information in project?

Hi @miyazato

Thanks for reaching out.
It seems that the error doesn't concern fetched links but the higher-level data field as shown in this error


//Property 'data' does not exist on type 'EmptyLinkField<"Document"> | FilledLinkToDocumentField<"creator", string, never>'.
                                              // Property 'data' does not exist on type 'EmptyLinkField<"Document">'.ts(2339)
  return <>...</>
}

If that is correct, then can you try to remove the Fetch links and try if it works?

Hi Fares

Thanks you for your reply.
Sorry, the error message seems to be different due to the simplification.
Below is the code that actually works.

type ProjectProps = {
  project: ProjectDocument<string>
}

const Project: NextPage<ProjectProps> = ({ project }) => {
  if( !isFilled.contentRelationship(project.data.creator) ) { // Need to validate again, even though it is obvious.
    return <></>
  }
  const creator = project.data.creator
  console.log('creator.data.name:', creator.data.name) // This work but show error in VScode
  /**
     (property) FilledLinkToDocumentField<"creator", string, never>.data?: undefined
     Object is possibly 'undefined'.ts(2532)
  */
  return (
    <></>
  )
}
export default Project

export const getStaticProps = async ({ params, previewData }: any /** I don't know what type.🥹 */) => {
  const client = createClient({ previewData })
  const project = await client.getByUID<ProjectDocument>('project', params.uid, {
    fetchLinks: ['creator.name', 'creator.face']
  })
  if( !isFilled.contentRelationship(project.data.creator) ) {
    return {
      notFound: true
    }
  }
  return {
    props: {
      project
    },
  }
}

I know that creator.data.name is accessible and works, but TypeScript don't know it.

1 Like

Hi @miyazato

Well, I have reported this issue to our dev team, and I will let you know in case of any updates.

Feel free to raise the issue in the open-source project

Hi @Fares

Thanks.

The types can be manually typed.
Not ideal, this problem was avoided.

type Simplify<T> = {
  [KeyType in keyof T]: T[KeyType]
}
type FetchKeyUnion<T extends string[] | readonly string[]> = T[number] // array to union type
type ProjectWithFetched<U extends FetchKeyUnion<T>> = RelationField<"project", string, Pick<ProjectDocument['data'], U>>

interface FeaturedProjectsDocumentDataProjectsItemFilled<U extends FetchKeyUnion<T>> extends FeaturedProjectsDocumentDataProjectsItem {
  project: ProjectWithFetched<U>
}
interface FeaturedProjectsDocumentDataFilled<U extends FetchKeyUnion<T>> extends FeaturedProjectsDocumentData {
  projects: GroupField<Simplify<FeaturedProjectsDocumentDataProjectsItemFilled<U>>>
}

type FeaturedProjectsDocumentWithLinks<U extends FetchKeyUnion<T>, Lang extends string = string> = PrismicDocumentWithoutUID<Simplify<FeaturedProjectsDocumentDataFilled<U>>, "featured-projects", Lang>

const projectFetchKeys = ['title', 'featuredMedia', 'abstract'] as const
type TProjectFetchKey = FetchKeyUnion<typeof projectFetchKeys>

export async function getStaticProps({ previewData }: any) {
  const client = createClient({ previewData })
  const featuredProjects = await client.getSingle<FeaturedProjectsDocumentWithLinks<TProjectFetchKey>>('featured-projects', {
    fetchLinks: projectFetchKeys.map(key => `project.${key}`)
  })

  return { props: { featuredProjects, }, }
}

type Props = {
  featuredProjects: FeaturedProjectsDocumentWithLinks<TProjectFetchKey>
}

Right, you appear to be using prismic-ts-codegen.

That tool does not generate TypeScript types for data fetched with fetchLinks/graphQuery, so you need to add those types yourself.

And I will keep you up-to-date in case of any changes.

Hi, @Fares

Ok, I understand prismic-ts-codegen does not have such a generics.

Thank you so much!

1 Like

You are welcome

Hey @miyazato @Fares I ran into this problem and I solved it.

For context, I am also using prismic-ts-codegen and a GraphQuery.

I looked at the isFilled.contentRelationship and noticed it accepted some generic type arguments, and this is working for me:

// contentRelationship: <TypeEnum, LangEnum, DataInterface>
if (isFilled.contentRelationship<'blog-author', string, BlogAuthorDocument['data']>(doc.data.authorLink)) {
  doc.data.authorLink.data?.avatar;
}
3 Likes

Hi @eddyvinck95

I used Type Argument and it worked!
Thanks a lot!

const projects = await client.getAllByType<ProjectDocument>('project', {
  fetchLinks: ['creator.name', 'creator.face']
})
if( isFilled.contentRelationship<'creator', string, Pick<CreatorDocument['data'], 'name' | 'face'>>(projects[0].data.creator) ) {
  projects[0].data.creator.data?.name // It's working well!
}

1 Like