Based on the example of Relay, it seems trivial to implement a list that read the data from the main query and extract the fragment data from the story ref.
Relay example

However, if we add a level of abstraction and need to deal with typing, it's a different story. Here is my example:
export default function Newsfeed({}) {
const data = useLazyLoadQuery<NewsfeedQueryType>(NewsfeedQuery, {});
const stories = data.topStories;
return (
<div className="newsfeed">
<StoryList stories={stories} />
</div>
);
}
// The way I found is to extract the node type myself and later cast it to StoryFragment$key, which works but I feel like it's a trick
type StoryNode = NewsfeedQuery$data['topStories'][0]
function StoryList(stories: StoryNode[]) { // <<< HERE I'm using the extracted StoryNode to access the data from the main query, however my Story component expect a StoryFragment$key. Which seems accepted, as an implicit cast occurs, the compiler doesn't complain.
return (
<>
{stories.map(story => (
<Story
key={story.id} // <<<<< HERE: I'd like to use data from the main query and not from the fragment
storyRef={story} // <<< HERE: Looks like an implicit cast occurs to StoryFragment$key
/>
)}
</>
);
}
function Story(storyRef: StoryFragment$key) {
// Use fragment expect the generate type StoryFragment$key
const story = useFragment(StoryFragment, storyRef)
...
}
The way I found is to extract the node type myself and later cast it to StoryFragment$key, which works, but I feel like it's a trick. Especially in a real case where usually the data looks more like:
topStories {
edges {
node {
id
...StoryFragment
}
}
}
In that case we ends with:
type TopStories = NonNullable<NewsfeedQuery$data['topStories']>;
type StoryEdge = NonNullable<TopStories['edges']>[0];
type StoryNode = NonNullable<StoryEdge>['node'];
Which is probably ok, however the cast to the fragment ref bother me, and I feel like something is not right.
What would be the optimal way to handle the typing properly in that case?