React-apollo: onCompleted callback typedef is not destructuring friendly

Created on 12 Oct 2018  ·  8Comments  ·  Source: apollographql/react-apollo

Intended outcome:
I'd like to have a destructuring-friendly type definition for component's onCompleted callback property.

Actual outcome:

I want to be able to destructure the onCompleted callback argument in a type-safe way:

<Query<{ foo: string }> query={GetFoo} onCompleted={({ foo }) => {}} />

This works at runtime, but TypeScript complains about the definition of the parameter passed to the onCompleted callback:

[ts] Type '{} | { foo: string; }' has no property 'foo' and no string index signature.

screen shot 2018-10-11 at 7 31 39 pm

How to reproduce the issue:

Check out this CodeSandbox: https://codesandbox.io/s/lpjj2pom0z

Version

Most helpful comment

@JoviDeCroock If I were to create a schema and connect Apollo, foo would indeed be a string. The data is there working properly, so that's awesome. No runtime issue 👍

The problem is that onCompleted is defined as

(data: TData | {}) => void

So TypeScript thinks the data object passed to the callback is either TData, which I defined as { foo: string } or, for no reason I can infer, an unindexed empty object {}.

Since that latter case is unindexed and does not contain my foo prop, destructuring fails static typing.

Does that make sense?

From what I can tell, the data param passed to the callback should always be TData, so in my PR, I just changed the definition to

(data: TData) => void

All 8 comments

Normally the callback provided in onCompleted is wrapped in an object named data. This implies the result from your completed Query. Since this is handled by Apollo you should be ensured of the answer or am i overlooking something here?

@JoviDeCroock If I were to create a schema and connect Apollo, foo would indeed be a string. The data is there working properly, so that's awesome. No runtime issue 👍

The problem is that onCompleted is defined as

(data: TData | {}) => void

So TypeScript thinks the data object passed to the callback is either TData, which I defined as { foo: string } or, for no reason I can infer, an unindexed empty object {}.

Since that latter case is unindexed and does not contain my foo prop, destructuring fails static typing.

Does that make sense?

From what I can tell, the data param passed to the callback should always be TData, so in my PR, I just changed the definition to

(data: TData) => void
| {}

Is also a pretty bad developer experience for people running stricter typescript setups since there isn't really a decent typeguard (that im aware of) for that typescript can infer itself compared to something like null or undefined.

@JoviDeCroock Is there anything you need me to change about this PR? Or can it be considered for merging?

@jozanza I'm not a contributor or a member, just trying to help out where I can PR seems fine for me personally. I think the Apollo team is a bit swamped with the upcoming event.

Ah sorry! Mybad.

Made a small comment on your PR of something i just thought about :)

For now, I define the typing for the argument directly to overcome this.

<Query<QueryEditCompany, QueryEditCompanyVariables>
  query={COMPANY}
  onCompleted={(data: QueryEditCompany) => {//do something}}
>
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dmitryyankowski picture dmitryyankowski  ·  3Comments

voodooattack picture voodooattack  ·  3Comments

Acentelles picture Acentelles  ·  3Comments

bdouram picture bdouram  ·  3Comments

notadamking picture notadamking  ·  3Comments