facebook / facebook/relay

Updatable list elements are not assignable to themselves

Open
#5,363 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
19k
Forks
1.9k
PR merge metrics
No merged PRs in 30d

Description

I have a component, with:

1. `useFragment` reading list of elements
2. mutation pushing a new element in updater

```tsx
// IN RENDER:
const mediaListCollection = useFragment(
graphql`
fragment AddToList_mediaListCollection on MediaListCollection {
lists {
...AddToList_updatable
status
entries {
id
...AddToList_assignable
}
}
}
`,
mediaListCollectionKey
)

// MUTATION:
{
updater: (store, response) => {
for (const list of mediaListCollection.lists ?? []) {
if (list != null && list.status === source.status) {
const { updatableData } =
store.readUpdatableFragment(
graphql`
fragment AddToList_updatable on MediaListGroup @updatable {
entries {
id
...AddToList_assignable
}
}
`,
list
)

if (response?.SaveMediaListEntry != null) {
updatableData.entries = [
...(list.entries?.filter((entry) => entry != null) ?? []),
response.SaveMediaListEntry,
]
}
}
}
}
}
```

I've noticed `useFragment` is causing performance issue and it doesn't have to read during render so I've moved it into the updater.

```tsx

// AFTER REFACTOR:
{
updater: (store, response) => {
const { updatableData: mediaListCollection } =
store.readUpdatableFragment(
graphql`
fragment AddToList_mediaListCollection on MediaListCollection
@updatable {
lists {
status
entries {
id
...AddToList_assignable
}
}
}
`,
mediaListCollectionKey
)

for (const list of mediaListCollection.lists ?? []) {
if (list != null && list.status === source.status) {
if (response?.SaveMediaListEntry != null) {
list.entries = [
...(list.entries?.filter((entry) => entry != null) ?? []),
response.SaveMediaListEntry,
]
}
}
}
}
}
```

But now I get a type error when setting:
```
Type '{ readonly id: string; } | null | undefined' is not assignable to type '{ readonly __typename: "MediaList"; readonly __id: string; readonly " $fragmentSpreads": FragmentRefs<"AddToList_assignable">; }'.
```

New generated type is missing `__typename` and `__id` fields:

```ts
export type AddToList_mediaListCollection$data = {
get lists(): ReadonlyArray<{
status: MediaListStatus | null | undefined;
get entries(): ReadonlyArray<{
readonly id: string; // <-- HERE ONLY ID IS GENERATED
} | null | undefined> | null | undefined;
set entries(value: ReadonlyArray<{
readonly __typename: "MediaList";
readonly __id: string;
readonly " $fragmentSpreads": FragmentRefs<"AddToList_assignable">;
}>);
} | null | undefined> | null | undefined;
set lists(value: []);
readonly " $fragmentType": "AddToList_mediaListCollection";
};
```

The OLD one is:
```ts
export type AddToList_mediaListCollection$data = {
readonly lists: ReadonlyArray<{
readonly $updatableFragmentSpreads: FragmentRefs<"AddToList_updatable">;
readonly entries: ReadonlyArray<{
readonly __typename: "MediaList";
readonly __id: string;
readonly id: string;
readonly " $fragmentSpreads": FragmentRefs<"AddToList_assignable">;
} | null | undefined> | null | undefined;
readonly status: MediaListStatus | null | undefined;
} | null | undefined> | null | undefined;
readonly " $fragmentType": "AddToList_mediaListCollection";
};
```

There's an easy workaround by selecting these missing fields manually
```
__typename
__id
```
This makes it work without runtime error, just the typescript error because the `AddToList_assignable` spread is missing

relay-compiler: 21.0.1

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in relay-compiler 21.0.1 by reproducing the shown @updatable fragment and assignable spread with the generated TypeScript types. Trace how the compiler handles nested updatable fields and fragment spreads; done means the generated entries setter accepts the assignable fragment type while retaining __typename and __id without manual selections.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.