Within our project, we are using the library apollo-angular. This library comes with predefined generic classes. One of these classes is called Query and takes two type parameters. These are its default values Query<T = {}, V = EmptyObject>.
API generator uses these classes to generate an API communication layer, one of these classes looks like this.
class GetPageTypesGQL extends Query<GetPageTypesQuery, GetPageTypesQueryVariables>
export type GetPageTypesQueryVariables = Exact<{ [key: string]: never }>;
export type GetPageTypesQuery = { __typename?: 'Query'; feedPageTypes: Array<string> };
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
In our app we had this type for a very long time, its purpose was simple. You provided a class that implemented the Query class and returned its first parameter type. And it looked like this.
export type QueryType<T> = T extends Query<infer Q, infer V> ? Q : never;
So for example you could use it like this.
type GetPageType = QueryType<GetPageTypesGQL> // and the type was GetPageTypesQuery
Unfortunately, this stopped working with the latest Typescript version 4.9.5. Currently, there is some kind of matching which compares Query<T = {}, V = EmptyObject> with Query<GetPageTypesQuery, GetPageTypesQueryVariables> and it results in a falsy value so the return type is always never.
All I came up with until now is
type QueryType<T, Q = any, V = any> = T extends Query<Q, V> ? Q : never;
but it always returns any type or never types, but never extracts the actual type of the first type parameter.
My question to you is, have you experienced something similar or have an idea what might be wrong here? Thank you!
It's possible that the change in TypeScript version 4.9.5 has affected the behaviour of your QueryType type alias. The issue may be related to how TypeScript resolves conditional types, which can be a bit tricky.
Here's a possible solution that should work for your use case:
The key difference is that instead of using two type parameters (Q and V), we're only using Q, and we're using infer to extract the type parameter from the Query class.
In this case, we're not concerned with the second type parameter V, so we can just use any as its default value. This should work as long as the Query class always has the same structure, regardless of the type parameters used.