I'm building a site using Prismic, Gatsby, and Typescript. Part of the appeal of Prismic is the slice feature, which allows you to create dynamic content sections that are easier for content editors to use. I've got a component called SliceZone
that maps through all the slices on a page:
SliceZone.tsx
import React from 'react';
import { ImageCta } from 'components/slices/call-to-action/image/ImageCta';
import { Columns } from 'components/slices/columns/Columns';
import { ContentBlock } from 'components/slices/content-block/ContentBlock';
import { Embed } from 'components/slices/embed/Embed';
import { TextHero } from 'components/slices/hero/text/TextHero';
import { Slider } from 'components/slices/slider/Slider';
import { Video } from 'components/slices/video/Video';
interface PageSlicesProps {
slices: any;
}
const PageSlices = ({ slices }: PageSlicesProps) => {
const sliceComponents = {
hero: TextHero,
slider: Slider,
content_block: ContentBlock,
video: Video,
columns: Columns,
embed: Embed,
image_call_to_action: ImageCta,
};
return slices.map((slice: any, index: number) => {
const SliceComponent = sliceComponents[slice.type];
if (SliceComponent) {
return <SliceComponent slice={slice} key={`slice-${slice.type}-${index}`} />;
}
});
};
interface SliceZoneProps {
bodyContent: any;
}
export const SliceZone = ({ bodyContent }: SliceZoneProps) => <PageSlices slices={bodyContent.body} />;
and I'm needing to appropriately type everything. However, I'm getting this error on SliceComponent
, specifically sliceComponents[slice.type]
:
Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ hero: ({ slice }: any) => Element; slider: ({ slice }: any) => Element; content_block: ({ slice }: any) => Element; video: ({ slice }: any) => Element; columns: ({ slice }: any) => Element; embed: ({ slice }: { ...; }) => Element; image_call_to_action: ({ slice }: any) => Element; }'.
How do I create an interface
that will appropriately type this? I'm sorry if my question is somewhat muddy, I'm new to React and to Typescript, so the language is a bit gone on me as of yet.
The issue is when your mapping of const sliceComponents =
. It's missing a signature, so the return type is implicitly any
.
As the error says.
You could make it explicit by saying you have a list of React class components/function components?
const sliceComponentsMap: {
[key: string]: typeof Component | FunctionComponent;
}
The |
is a union operator. This says it can be either of these types.
But I think would be better to create a common slice class that extends the React Component class.
export interface sliceCommonProps {
id: string;
}
// Interface for base slice props
export interface sliceProps<P> {
sliceType: string;
sliceData: P & sliceCommonProps; // This "&" joins the 'Generic' param P with the common props interface
}
export interface sliceState {}
// Base class with two generic params (P, S) for passing specific component props and state interfaces later
export class SliceComponent<
P = { [key: string]: any },
S = { [key: string]: any }
> extends Component<sliceProps<P>, sliceState> {}
And have your slice components extend that.
export interface ImageCtaProps {
imageUrl: string;
}
export class ImageCta extends SliceComponent<ImageCtaProps> {
render() {
const { id, imageUrl } = this.props.sliceData;
return <div>{imageUrl}</div>;
}
}
Then when you map them to their components you can say they are of type SliceComponent. And compiler now knows the component should have the correct props.
const sliceComponents: {[key: string]: typeof SliceComponent;} = {
hero: TextHero,
...
};
slices.map((slice, index) => {
const SliceComponent = sliceComponents[slice.type];
return <SliceComponent sliceData={slice} sliceType={slice.type} />
);
});