Improving Type-Safety for Prismic Data
- Published
- Reading time
- 4 minutes
As a developer building websites with TypeScript, I'm always on the lookout for ways to improve the type-safety and reliability of my code. One area where type-safety can be particularly challenging is when working with content from an external data source, including a headless content management system (CMS) like Prismic. Recently, I wrote some improvements into a code snippet that has significantly enhanced my development experience by ensuring that link fields in Prismic are more type-safe. I found the original inspiration for this code from a post by angeloashmore on the Prismic community forum.
The Challenge
Prismic's flexibility in content modelling is fantastic, but with flexibility comes the risk of inconsistencies and potential runtime errors. One common issue is verifying that a link field actually contains the expected content relationship. Without strict type-checking, it's easy to mistakenly assume a link points to a specific document type when it doesn't, leading to bugs and a frustrating debugging process.
The Solution
The code snippet below acts as a type-safe wrapper around Prismic's isFilled.contentRelationship
function. By leveraging TypeScript's powerful type system, it ensures that link fields are not only filled but also contain the expected field IDs for a given document type. This is incredibly useful for validating that a link field is correctly pointing to the intended document type and contains the necessary fields.
/**
* Type-safe wrapper around Prismic's `isFilled.contentRelationship` function to make link fields more type-safe.
* You'll want to make sure that your Prismic client is set up to handle typed content.
*
* If using Next.js and Slice Machine, you can use the `@prismicio/client` package to set up typed content.
* @see {@link https://prismic.io/docs/typescript-nextjs} for more information.
*
* Prismic also offers support via the `prismic-ts-codegen` package.
* @see {@link https://prismic.io/docs/technical-reference/prismic-ts-codegen} for more information.
*/
import {
type Content,
type FilledContentRelationshipField,
isFilled,
type LinkField,
} from "@prismicio/client";
// Extracts the data of a specific document type from Prismic content
type DocumentData<TDocumentType extends Content.AllDocumentTypes["type"]> =
Extract<Content.AllDocumentTypes, { type: TDocumentType }>["data"];
/**
* Checks if a link field in Prismic is a filled content relationship field and contains the specified field IDs.
* This is useful for checking if a link field is a valid link to a specific document type and contains the specified field IDs.
* This acts as a wrapper around the `isFilled.contentRelationship` function from Prismic to make link fields more type-safe.
*
* @template TDocumentType - The type of the document.
* @template TFieldID - The field IDs to check within the document data.
* @param {LinkField} linkField - The link field to check.
* @param {TDocumentType} documentType - The type of the document to match against.
* @param {TFieldID[]} fieldIDs - The field IDs to check within the document data.
* @returns {linkField is FilledContentRelationshipField & { data: { [P in keyof DocumentData<TDocumentType> as P extends TFieldID ? P : never]: DocumentData<TDocumentType>[P]; } }} - Returns true if the link field is a filled content relationship field and contains the specified field IDs.
*
* @example
* ```ts
* import { isFilledLinkedContent } from "./is-filled-linked-content";
*
* const isValidLink = isFilledLinkedContent(linkField, "article", ["title"]);
* ```
*/
export function isFilledLinkedContent<
TDocumentType extends Content.AllDocumentTypes["type"],
TFieldID extends keyof DocumentData<TDocumentType>,
>(
linkField: LinkField,
documentType: TDocumentType,
fieldIDs: TFieldID[],
): linkField is FilledContentRelationshipField & {
data: {
[P in keyof DocumentData<TDocumentType> as P extends TFieldID
? P
: never]: DocumentData<TDocumentType>[P];
};
} {
return (
// Check if the link field is a filled content relationship field
isFilled.contentRelationship(linkField) &&
// Check if the link field's type matches the specified document type
linkField.type === documentType &&
// Ensure the link field's data is an object and not null
typeof linkField.data === "object" &&
linkField.data !== null &&
// Check if all specified field IDs exist in the link field's data
fieldIDs.every(
(fieldID) => fieldID in (linkField.data as Record<string, unknown>),
)
);
}
Why This Matters
Using this function, isFilledLinkedContent
, we can confidently check if a link field in Prismic is a valid content relationship field and contains the specified field IDs. This type-safe check eliminates the guesswork and potential errors, making our TypeScript-based development smoother and more robust.
Enhanced Developer Experience with Slice Machine
Prismic's latest improvements with Slice Machine have already made the developer experience more enjoyable. Slice Machine allows us to visually build components (slices) and seamlessly integrate them into our projects. It's a game-changer for creating modular and reusable content blocks.
However, the type-safety provided by this specific code snippet takes it to the next level. It ensures that our integrations with Prismic are not just convenient but also reliable and maintainable. By combining the intuitive interface of Slice Machine with the rigorous type-checking of TypeScript, we can build websites that are both user-friendly and developer-friendly.
Conclusion
Incorporating type-safe practices like isFilledLinkedContent
into our Prismic projects is essential for building robust applications. It enhances our confidence in the code, reduces the likelihood of runtime errors, and ultimately leads to a better development experience. As we continue to embrace tools like Slice Machine and TypeScript, it's these small but impactful improvements that make a significant difference in our workflow.