relay icon indicating copy to clipboard operation
relay copied to clipboard

Shouldn't react-compiler generate query$data type with the " $data" prop too?

Open yuliswe opened this issue 10 months ago • 4 comments

I'm confused why the generated types from query is missing the " $data" property. Like this:

export type NewProductReviewScreenInitialQuery$data = {
  readonly product: {
    readonly " $fragmentSpreads": FragmentRefs<"NewProductReviewScreen_ProductFragment">;
  } | null | undefined;
  readonly review: {
    readonly " $fragmentSpreads": FragmentRefs<"NewProductReviewScreen_ReviewFragment">;
  } | null | undefined;
};

The above was generated from a usePreloadedQuery like this

export const newProductReviewScreenInitialQuery = graphql`
  query NewProductReviewScreenInitialQuery(
    $reviewId: UUID!
    $productId: UUID!
  ) {
    product(id: $productId) {
      ...NewProductReviewScreen_ProductFragment
    }
    review(id: $reviewId) {
      ...NewProductReviewScreen_ReviewFragment
    }
  }
`;


  const fragRef = usePreloadedQuery(
    newProductReviewScreenInitialQuery,
    queryRef
  );

So the type for fragRef.product would be

{
    readonly " $fragmentSpreads": FragmentRefs<"NewProductReviewScreen_ProductFragment">;
  }

I think this is wrong.

The @types/react-relay/relay-hooks/useFragment.d.ts contains

export function useFragment<TKey extends KeyType>(
    fragmentInput: GraphQLTaggedNode,
    fragmentRef: TKey,
): KeyTypeData<TKey>;

and @types/react-relay/relay-hooks.helpers.td.ts contains

export type KeyType<TData = unknown> = Readonly<{
    " $data"?: TData | undefined;
    " $fragmentSpreads": FragmentType;
}>;

export type KeyTypeData<TKey extends KeyType<TData>, TData = unknown> = Required<TKey>[" $data"];

This is implying to me that the useFragment requires the fragRef.product to be generated with the corresponding " $data" property. Otherwise, I always have to explicitly provide the ...$key type in order to use useFragment:

  const productProfileData = useFragment<NewProductReviewScreen_ProductFragment$key>(
    graphql`
      fragment NewProductReviewScreen_ProductFragment on Product {
        ofVendor {
          ofUser {
            firstName
          }
        }
      }
    `,
    fragRef.product
  );

Without the <NewProductReviewScreen_ProductFragment$key> explicitly provided, the return value is unknown.

Versions:

@types/[email protected] @[email protected] @[email protected]

yuliswe avatar Feb 02 '25 21:02 yuliswe

Are you observing that a type param is currently required for useFragment in TypeScript? If so, I believe that is expected, and a current limitation. If you have a concrete proposal for a way we could generate types which would avoid that, I'd be open to hearing it.

One approach is currently being explored in https://github.com/facebook/relay/issues/4884

captbaritone avatar Feb 04 '25 08:02 captbaritone

Thanks for the explanation. I thought my configuration was wrong.

yuliswe avatar Feb 05 '25 07:02 yuliswe

Hi @captbaritone, currently is there any technical difficulty for simply generating the type as

export type NewProductReviewScreenInitialQuery$data = {
  readonly product: NewProductReviewScreen_ProductFragment$key | null | undefined;
};

instead of the current behavior of generating this:

export type NewProductReviewScreenInitialQuery$data = {
  readonly product: {
    readonly " $fragmentSpreads": FragmentRefs<"NewProductReviewScreen_ProductFragment">;
  } | null | undefined;
};

?

The former seems that it will fit right into the KeyTypeData<TKey> type.

yuliswe avatar Feb 05 '25 07:02 yuliswe

How would your example look if there were other fields being read off of product in addition to the fragment spread?

captbaritone avatar Feb 14 '25 17:02 captbaritone