Add TypeScript Type Checks to RouteData fields

> Inspired by error: Conversion of type may be a mistake because neither type sufficiently overlaps with the other.
Cover Image for Add TypeScript Type Checks to RouteData fields

All the world's a type and we are all merely types

The Problem

RouteData is an interface defined as:


_16
export interface RouteData {
_16
name: string;
_16
displayName?: string;
_16
fields?: {
_16
[name: string]: Field;
_16
};
_16
databaseName?: string;
_16
deviceId?: string;
_16
itemLanguage?: string;
_16
itemVersion?: number;
_16
layoutId?: string;
_16
templateId?: string;
_16
templateName?: string;
_16
placeholders: PlaceholdersData;
_16
itemId?: string;
_16
}

Whereby this:


_1
const foo = useSitecoreContext().sitecoreContext.route?.fields;

Corresponds to:


_3
(property) RouteData.fields?: {
_3
[name: string]: Field<GenericFieldValue>;
_3
} | undefined

In other words, RouteData.fields acts as a kind of arbitrary dictionary.

Now imagine that I have defined a custom type (the full type actually has 25+ fields):


_8
export type Publication = {
_8
id?: string;
_8
url?: string;
_8
fields: {
_8
abstract: Field<string>;
_8
attachment: FileField;
_8
};
_8
}

I'd like to be able to do this:


_1
const foo = useSitecoreContext().sitecoreContext.route?.fields as Publication['fields'];

However, I get this IDE / build error:


_2
Conversion of type '{ [name: string]: Field<GenericFieldValue>; } | undefined' to type '{ abstract: Field<string>; attachment: FileField; }' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
_2
Type '{ [name: string]: Field<GenericFieldValue>; }' is missing the following properties from type '{ abstract: Field<string>; attachment: FileField; }': abstract, attachment

If I do as the error suggests, the IDE and build gods are pleased:


_1
const bar = (useSitecoreContext().sitecoreContext.route?.fields as unknown) as Publication['fields'];

One Solution

Let's say you need a component that renders a header for a "blog" page. The header should display the title of the blog and the type of blog. Both fields are specified on the page item (and therefore the route). The blog type is a list field that is a reference to a custom type called BlogType. BlogType has a single field called Name. The header component should be able to render the title and the name of the blog type.

BlogHeader.tsx

_32
import { Text, useSitecoreContext, Field } from '@sitecore-jss/sitecore-jss-nextjs';
_32
import { Website } from 'lib/component-props/model';
_32
_32
type BlogHeaderRouteFields = Website.Project.Main.PageTypes.Blogs.Publication['fields'];
_32
_32
const BlogHeader = (): JSX.Element => {
_32
const { sitecoreContext } = useSitecoreContext();
_32
_32
if (!sitecoreContext || !sitecoreContext?.route?.fields) {
_32
return <></>;
_32
}
_32
_32
const blogHeaderRouteFields = (sitecoreContext.route.fields as unknown) as BlogHeaderRouteFields;
_32
_32
return (
_32
<div>
_32
<h1 className="blog-header">
_32
<Text field={blogHeaderRouteFields.title as Field<string>} />
_32
</h1>
_32
{blogHeaderRouteFields.blogTypes?.value && (
_32
<div className="blog-type">
_32
<Text
_32
field={blogHeaderRouteFields.blogTypes.value[0]?.fields?.name as Field<string>}
_32
editable={false}
_32
/>
_32
</div>
_32
)}
_32
</div>
_32
);
_32
};
_32
_32
export default BlogHeader;

The Better Solution

sitecoreContext has no knowledge of what RouteData it has; specifically, fields. It defaults to Record<string, string | Field | Item | Item[]>, which means we need to assert what is in it when we use it.

The better solution to this would be to have a generic type for RouteData that allows us to specify the type of fields. If generics were supported we could do this:


_1
const { sitecoreContext } = useSitecoreContext<MyRouteFields>();

This is cleaner and more less prone to errors because assertions don't need to be added whenever route fields are referenced. I am also not the first one to notice or ask for this:

https://github.com/Sitecore/jss/issues/1016

The OP describes the ask perfectly and offers a temporary solution until someone takes the precious time to submit a PR.

-MG


More Stories

Cover Image for Don't Ignore the HttpRequestValidationException

Don't Ignore the HttpRequestValidationException

> Doing so could be... potentially dangerous

Cover Image for Sitecore Symposium 2022

Sitecore Symposium 2022

> What I'm Watching 👀

Cover Image for Troubleshooting 502 Responses in Azure App Services

Troubleshooting 502 Responses in Azure App Services

> App Services don't support all libraries

Cover Image for Hello World

Hello World

> Welcome to the show

Cover Image for Year in Review: 2022

Year in Review: 2022

> Full steam ahead

Cover Image for Symposium 2022 Reflections

Symposium 2022 Reflections

> Sitecore is making big changes

Cover Image for On Sitecore Development

On Sitecore Development

> Broadly speaking

Cover Image for JSS + TypeScript Sitecore Project Tips

JSS + TypeScript Sitecore Project Tips

> New tech, new challenges

Cover Image for SPE Script Performance & Troubleshooting

SPE Script Performance & Troubleshooting

> Script never ends or runs too slow? Get in here.

Cover Image for Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

> It's probably time to overhaul your processes

Cover Image for Azure PaaS Cache Optimization

Azure PaaS Cache Optimization

> App Services benefit greatly from proper configuration

Cover Image for Tips for New Sitecore Developers

Tips for New Sitecore Developers

> If I had more time, I would have written a shorter letter

Cover Image for Script: Boost SIF Certificate Expiry Days

Script: Boost SIF Certificate Expiry Days

> One simple script that definitely won't delete your system32 folder

Cover Image for NextJS: Access has been blocked by CORS policy

NextJS: Access has been blocked by CORS policy

> CORS is almost as much of a nuisance as GDPR popups

Cover Image for Critical Security Bulletin SC2024-001-619349 Announced

Critical Security Bulletin SC2024-001-619349 Announced

> And other scintillating commentary

Cover Image for NextJS: Short URL for Viewing Layout Service Response

NextJS: Short URL for Viewing Layout Service Response

> Because the default URL is 2long4me

Cover Image for On Mentorship and Community Contributions

On Mentorship and Community Contributions

> Reflections and what I learned as an MVP mentor

Cover Image for Security Series: App Service IP Restrictions

Security Series: App Service IP Restrictions

> How to manage IP rules "at scale" using the Azure CLI

Cover Image for Ideas For Docker up.ps1 Scripts

Ideas For Docker up.ps1 Scripts

> Because Docker can be brittle

Cover Image for NextJS: Unable to Verify the First Certificate

NextJS: Unable to Verify the First Certificate

> UNABLE_TO_VERIFY_LEAF_SIGNATURE

Cover Image for JSS: Reducing Bloat in Multilist Field Serialization

JSS: Reducing Bloat in Multilist Field Serialization

> Because: performance, security, and error-avoidance

Cover Image for NextJS/JSS Edit Frames Before JSS v21.1.0

NextJS/JSS Edit Frames Before JSS v21.1.0

> It is possible. We have the technology.

Cover Image for Content Editor Search Bar Not Working

Content Editor Search Bar Not Working

> Sometimes it works, sometimes not

Cover Image for How to Run Old Versions of Solr in a Docker Container

How to Run Old Versions of Solr in a Docker Container

> Please don't make me install another version of Solr on my local...

Cover Image for On Sitecore Stack Exchange (SSE)

On Sitecore Stack Exchange (SSE)

> What I've learned, what I see, what I want to see

Cover Image for Super Fast Project Builds with Visual Studio Publish

Super Fast Project Builds with Visual Studio Publish

> For when solution builds take too long

Cover Image for Tips for Forms Implementations

Tips for Forms Implementations

> And other pro tips

Cover Image for Early Returns in React Components

Early Returns in React Components

> When and how should you return early in a React component?