Hello. I am trying to solve a challenge which should be fairly simple, but without full success yet.
My goal is to wrap a certain types of slices in a container. I need to preprocess the slices and create a wrapper div for slices of type "content_block" which comes one after another.
The outcome I want is below. What is the best way to approach it in Next.js with Typescript?
With this part in particular. "Element implicitly has an 'any' type because expression of type 'string' can't be used to index type". How can I fix this line?
Seeing the TS error might help me frame my response a little better. However, I'll take a shot here.
Let me think through your code:
const processedSlices = [];
// declare an empty constant array to hold slices
let contentBlockGroup: ContentBlockSlice[] = [];
// declare another empty array to hold ContentBlockSlices
// loop through each slice
// is index needed here?
page.data.slices.forEach((slice, index) => {
// entering the loop
// check slice_type
// if it's 'content_block' add it to contentBlockGroup
if (slice.slice_type === 'content_block') {
contentBlockGroup.push(slice);
} else {
// if it's not content_block
// check if contentBlockGroup's length is truthy
if (contentBlockGroup.length) {
// there's something in contentBlockGroup
// so let's add it to processedSlices
// it looks like processedSlices is meant to hold an object
// rather than slices
processedSlices.push({
slice_type: 'content_block_group',
items: contentBlockGroup,
});
// object has been added to processedSlices
// reset contentBlockGroup to an empty array
contentBlockGroup = [];
}
// if slice_type wasn't content_block
// and contentBlockGroup's length is falsey
// add the slice object to processedSlices
// (different than the other object pushed above)
processedSlices.push(slice);
}
});
// this next bit seems like a duplicate block
// perhaps this isn't needed?
if (contentBlockGroup.length) {
processedSlices.push({
slice_type: 'content_block_group',
items: contentBlockGroup,
});
}
/*
* Thoughts. Type processedSlices. Let TS know what it will/could hold
* const processed: Array<ContentBlockSlice | all your other slice types | {slice_type: string, items: ContentBlockSlice[]}> = []
* I'm certain my TS noobness should use a generic in the example above
*/
return (
<Main>
{processedSlices.map((slice, index) => {
if (slice.slice_type === 'content_block_group') {
return (
<div key={index} className='wrapper'>
{slice.items.map((contentBlockSlice, cbIndex) => (
<ContentBlock
index={cbIndex}
key={cbIndex}
slices={slice.items}
slice={contentBlockSlice}
context={null}
/>
))}
</div>
);
} else {
const Component = components[slice.slice_type];
return Component ? <Component key={index} slice={slice} /> : null;
}
})}
</Main>
);
the problem is with the below line of code:
const Component = components[slice.slice_type];
This is the full typescript message
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ articles: ComponentType; career_about: ComponentType; case_studies_latest: ComponentType<...>; ... 24 more ...; values: ComponentType<...>; }'.
No index signature with a parameter of type 'string' was found on type '{ articles: ComponentType; career_about: ComponentType; case_studies_latest: ComponentType<...>; ... 24 more ...; values: ComponentType<...>; }'.
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ articles: ComponentType; career_about: ComponentType; case_studies_latest: ComponentType<...>; ... 24 more ...; values: ComponentType<...>; }'.
No index signature with a parameter of type 'string' was found on type '{ articles: ComponentType; career_about: ComponentType; case_studies_latest: ComponentType<...>; ... 24 more ...; values: ComponentType<...>; }'.
const Component = components[slice.slice_type];
This Typescript message is referring to this single line.
I don't think I have a good answer, but here's something I was able to "figure out."
I was able to get the TS error to go away but recasting the type of slice. There is probably a good way to do this, but I certainly don't know it. However, to replicate this, I made a slice object and used a Hero slice as an example. If this was "the solution," you'd have to constantly update this to check for all your slice types. Not ideal by a longshot.
const slice = {
slice_type: 'hero'
} as unknown as HeroSlice | ContentSlice // etc
const Component = components[slice.slice_type]
For "hahas" you could check it out and see if it helps.