zirkelc
zirkelc

Reputation: 1723

Typescript: get type of first property of type

I get a nested object as result of a GraphQL query with the query name as first-level key and the actual data on the second level:

{
    getProduct: {
        id: "1",
        name: "test",
    }
}

In my query function I automatically extract the first key of the object and return the value of getProduct. However, I would like to infer the type of the first key as the return value of the query function.

type QueryReturn = FirstElement<GetProductQuery>; // should be { id: string; name: string }

All solutions I've found on the Internet infer the Head or Tail of an Array or Function.

Upvotes: 3

Views: 2433

Answers (4)

LIIT
LIIT

Reputation: 594

depending on your requirements, one can also choose to alias his queries to normalize their result.

Take following code :

export const MyComponent_FooEntity_QueryDocument = graphql(`
  query MyComponent_FooEntity_Query($id: Id!) {
    findMyFoo(id: $id) {
      # Fields
    }
  }
`)

export const MyComponent_BarEntity_QueryDocument = graphql(`
  query MyComponent_BarEntity_Query($id: Id!) {
    findMyBar(id: $id) {
      # Fields
    }
  }
`)

You can change it to :

export const MyComponent_FooEntity_QueryDocument = graphql(`
  query MyComponent_FooEntity_Query($id: Id!) {
    queryResult: findMyFoo(id: $id) {
      # Fields
    }
  }
`)

export const MyComponent_BarEntity_QueryDocument = graphql(`
  query MyComponent_BarEntity_Query($id: Id!) {
    queryResult: findMyBar(id: $id) {
      # Fields
    }
  }
`)

Like so, you can access your data type in a normalized way :

  import type { ResultOf } from '@graphql-typed-document-node/core';

  type QueryResult = ResultOf<
    typeof MyComponent_BarEntity_QueryDocument
  >['queryResult']

or a utility function :

export const getQueryResult = <R,>(query: { readonly queryResult: R }): R => query.queryResult

Upvotes: 0

devunt
devunt

Reputation: 366

Bit late. if you can assure that you always have single key in the outmost type, you can do it by utilizing keyof operator and indexed access type.

This will behave strangely if you have no keys or more than one key in the outmost type, so make sure you always have the exact one key in all scenario.

Example:

type Unwrap<T> = T[keyof T];

type QueryReturn = Unwrap<GetProductQuery>;

Playground Link

Upvotes: 2

lilBunnyRabbit
lilBunnyRabbit

Reputation: 60

I'm a bit late to the party but I saw that you were using graphql-codegen. I had a similar problem which I solved with:

type DataType = {
  __typename?: string;
};

type FilteredQuery<T> = {
  [P in keyof T as T[P] extends DataType ? P : never]: T[P];
};

type QueryData<T> = FilteredQuery<T>[keyof FilteredQuery<T>];

TypeScript playground example.

Upvotes: 4

JustinTRoss
JustinTRoss

Reputation: 787

Am I understanding correctly that you just want to get to the product type without the getProduct alias in the middle?

I get the hunch this extra level is there because you are aliasing your query. Regardless, there is no first key in an object. The way to drill down would be to specify the key. This can all be made much easier by auto-generating types with a tool like graphql-autogen.

Upvotes: 0

Related Questions