Correctly coerce “unknown” GraphQL response into type

Correctly coerce “unknown” GraphQL response into type


0

I am using the graphql-request library to fetch data froma GraphQL endpoint.
The endpoint returns some information about a user, based on a certain token associated to that user.

Here is the type I’ve defined:

export type PlayerInfo = {
    country: string,
    name: string|undefined,
    email: string,
    id: string,
}

Here are the two approaches I’ve tried so far (represented by the two functions getUserInfoOld and getUserInfo):

import { gql, GraphQLClient, request  } from 'graphql-request'
import { PlayerInfo } from './AuthClientInterface'
import type { TypedDocumentNode } from '@graphql-typed-document-node/core'
import { parse } from 'graphql'

type UserInfoResponse = {
    getUserInfo: {
      country: string,
      name: string|undefined,
      email: string,
      id: string,
    } 
}

class GraphQLClient {

  async getUserInfoOld(token: string): Promise<PlayerInfo> {
    const query = gql`
      query GetUserInfo($input: userInfoInput) {
        getUserInfo(input: $input) {
          country
          email
          id
          name
        }
      }
    `

    const input = {
      "input": {
        "accessToken": token
      }
    }

    const host = 'https://host.example/'
    const gqClient = new GraphQLClient(host)
    const result = await gqClient.request(query, input) as any // this works but I don't like it

    return result.getUserInfo as PlayerInfo
  }

  async getUserInfo(token: string): Promise<PlayerInfo> {
    const host = 'https://host.example/'

    const query: TypedDocumentNode<{ getUserInfo: UserInfoResponse }, Record<any, any>> = parse(gql`
      query GetUserInfo($input: userInfoInput) {
        getUserInfo(input: $input) {
          country
          email
          id
          name
        }
      }
    `)
  
    const input = {
      "input": {
        "accessToken": token
      }
    }

    const data = await request(host, query, input)

    return data.getUserInfo as PlayerInfo // this does not work: why?
    return data.getUserInfo as any // this works but I would like to avoid it
  }

}

export default GraphQLClient

Now, the first approach in works, but I don’t like it because I find it’s very much against Typescript’s whole concept to just cast any into the proper type, and just hope that it works.

Whic is why, after taking a look at this example, I tried working with TypedDocumentNode, adn tried creating an appropriate response type UserInfoResponse which then could be cast as PlayerInfo.

Note that when I add console.log(data.getUserInfo) right before the return statement of the second function, I see exactly what I would need:

{
  country: 'DE',
  email: '[email protected]',
  id: '12345678',
  name: 'John Doe'
}

So I don’t get why Typescript is giving me this error when I try to do return data.getUserInfo as PlayerInfo, when data.getUserInfo contains indeed the right fields:

Conversion of type 'UserInfoResponse' to type 'PlayerInfo' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type 'UserInfoResponse' is missing the following properties from type 'PlayerInfo': country, name, email, id

I even tried changing the UserInfoResponse type to

type UserInfoResponse = {
    getUserInfo: PlayerInfo
}

but this didn’t help either.

What am I missing or doing wrong here?


Load 5 more related questions


Show fewer related questions

0



Leave a Reply

Your email address will not be published. Required fields are marked *