Relay: Flow: property $refType is missing in object

Created on 3 Apr 2018  ยท  15Comments  ยท  Source: facebook/relay

Hello. ๐Ÿ‘‹ We use generated Flow types in our functions like this for example:

export default (room: ?BeddingInfo_room): string => {
  // ...
};

Problem is that Relay is generating Flow types with +$refType:

import type { ConcreteFragment } from 'relay-runtime';
import type { FragmentReference } from 'relay-runtime';
declare export opaque type BeddingInfo_room$ref: FragmentReference;
export type BeddingInfo_room = {|
  +type: ?string,
  +maxPersons: ?number,
  +bedding: ?$ReadOnlyArray<?{|
    +type: ?string,
    +amount: ?number,
  |}>,
  +$refType: BeddingInfo_room$ref,   // <<<
|};

This, unfortunately, complicates our testing because we cannot use simple plain objects (without the $refType):

expect(
  // Flow error here (vv) because Relay $refType is missing in the object
  formatBeddingInfo({
    type: 'Single Room',
    maxPersons: 1,
    bedding: [
      {
        type: 'Single Bed(s)',
        amount: 1,
      },
    ],
  }),
).toMatchSnapshot();

I don't really understand why is this property necessary but is it possible to make it at least optional? Or how should we test it properly? It was fine before version 1.5... Error message:

Cannot call formatBeddingInfo with object literal bound to room because property $refType is missing in object
literal [1] but exists in BeddingInfo_room [2].

     app/hotels/src/singleHotel/roomList/__tests__/formatBeddingInfo.test.js
      4โ”‚
      5โ”‚ it('formats bedding information', () => {
      6โ”‚   expect(
 [1]  7โ”‚     formatBeddingInfo({
      8โ”‚       type: 'Single Room',
      9โ”‚       maxPersons: 1,
     10โ”‚       bedding: [
     11โ”‚         {
     12โ”‚           type: 'Single Bed(s)',
     13โ”‚           amount: 1,
     14โ”‚         },
     15โ”‚       ],
     16โ”‚     }),
     17โ”‚   ).toMatchSnapshot();
     18โ”‚ });
     19โ”‚

     app/hotels/src/singleHotel/roomList/formatBeddingInfo.js
 [2]  7โ”‚ export default (room: ?BeddingInfo_room): string => {

Most helpful comment

Hey, sorry for not responding here earlier.

To unblock your tests for now, you can do something like:

// Maybe define this in some utility
const mockRefType: any = null;

expect(
  // Flow error here (vv) because Relay $refType is missing in the object
  formatBeddingInfo({
    $refType: mockRefType,
    type: 'Single Room',
    maxPersons: 1,
    bedding: [
      {
        type: 'Single Bed(s)',
        amount: 1,
      },
    ],
  }),
).toMatchSnapshot();

I do think there should be more work done on testability and making the testing more ergonomic.

All 15 comments

@sibelius Oh, that would probably solve my problem. But is it exported outside of the react-relay package? I am not able to find it (or figure out how to use it correctly).

EDIT: I am not sure about that. That won't solve my issue since the $refType is still required (?).

@mrtnzlml did you find a solution for this? I believe this issue is related as well: https://github.com/facebook/relay/issues/2316

@juhaelee I did not. We are just ignoring the errors for now until we find a better solution. I was quite surprised that no one noticed this change so 1) no one upgraded or 2) we are testing it wrong... :)

@mrmlnc I see, I also seemed to get flow errors on mutations as well ever since the upgrade :/

@juhaelee, I think you wanted to mention @mrtnzlml :)

Hey, sorry for not responding here earlier.

To unblock your tests for now, you can do something like:

// Maybe define this in some utility
const mockRefType: any = null;

expect(
  // Flow error here (vv) because Relay $refType is missing in the object
  formatBeddingInfo({
    $refType: mockRefType,
    type: 'Single Room',
    maxPersons: 1,
    bedding: [
      {
        type: 'Single Bed(s)',
        amount: 1,
      },
    ],
  }),
).toMatchSnapshot();

I do think there should be more work done on testability and making the testing more ergonomic.

My theory for the root cause is the flow type for $RelayProps (and therefore createFragmentContainer) is incorrect: https://github.com/facebook/relay/issues/2443. If I'm right, the fix might be pretty simple.

There is actually a workaround described in the relay doc:

Applied to a fragment definition, @relay(mask: false) changes the generated Flow types to be better usable when the fragment is included with the same directive. The Flow types will no longer be exact objects and no longer contain internal marker fields.

To be clear, you need to add @relay(mask: false) both when defining the fragment:

fragment Todo_todo on Todo @relay(mask: false)

and when using it:

...Todo_todo @relay(mask: false)

You will find a working example in this repository.

this PR could have solved this https://github.com/facebook/relay/pull/2293

try on master

@sibelius it did not. Just upgraded from 1.4.1 to 2.0.0 and we have hundreds of these flow errors now. the mockRefType workaround that @kassens suggested works, but I can't just go through each error and add this one by one. This is a big problem for us. The same goes for $fragmentRefs

Before I switched to TypeScript, I was manually fixing generated types as a workaround.

@steida By the way, then how did you solved it in TS?

I have a generic (NoFragmentRefs<T>) to cleanup recursively the references and I use it for component data and to prepare the fixtures.

@artola Check here https://github.com/este/este

Check out https://github.com/relayjs/relay-examples/tree/master/todo, @Stephen2 has been working with me to figure out the typing in OSS. The types exported from flow-typed seem to be a bit off and he's been looking in fixing them.

I'm pretty sure they work as defined in relay iself. We use them all over the place internally.

Was this page helpful?
0 / 5 - 0 ratings