UPDATE: Minimum example code reproduction
I have auto-generated code (Graphql-Codegen) that generates the following types. I've condensed it for simplicity.
export type AccessControlList = {
__typename?: 'AccessControlList';
id: Scalars['Int'];
subscriber: Scalars['Int'];
subscriberRel: MasterSubscriberData;
};
export type Query = {
__typename?: 'Query';
workOrders: Array<WorkOrdersGroupBy>;
viewer: AccessControlAccounts;
};
Since I am only interested in the viewer for this example, I select it by using Typescript Pick
export type ViewerType = Pick<Query, 'viewer'>;
It also auto-generates specialized types for queries that need some, but not all of the possible fields in AccessControlAccounts, and what fields I need vary. I omit the type definition of MasterSubscriberData here but it can be inferred below (but not really needed for this example)
Different queries need to access different portions of the Query structure and thus there are different types generated for each different query, e.g.
export type LeadSourcesQuery =
{ __typename?: 'Query',
viewer: { __typename?: 'AccessControlAccounts',
AccessControlList: Array<{ __typename?: 'AccessControlList',
subscriberRel: { __typename?: 'MasterSubscriberData',
LeadSources: Array<{ __typename?:
'LeadSources', id: number> }
}>
}
};
I'm trying to create a generic function that can return a reference to deeply nested objects commonly accessed, regardless of the subquery types passed (e.g. LeadSourcesQuery
, etc). What I came up with works at runtime but fails Typescript static checking.
// expected subscriberRel return type
type SubRetType<T extends ViewerType> =
T["viewer"]["AccessControlList"][number]["subscriberRel"];
// returning a reference to subscriberRel to avoid doing this nesting everywhere
export const subscriber = <T extends ViewerType>(
result: T | undefined
): SubRetType<T> | undefined => {
if (!result) return undefined;
return result.viewer?.AccessControlList.at(0)?.subscriberRel;
};
Whenever I call this code in src I get the following Typescript error I understand the is related to type compatibility.
Argument of type 'ListCustomerTypesQuery | undefined' is not assignable to parameter of type 'ViewerType | undefined'.ts(2345) Types of property 'viewer' are incompatible. ...is missing the following properties from type AccessControlAccounts
I'm just not sure how to help Typescript understand that whatever T
I pass it will always be a sub-type of Query
. However, by not using extends
on the super-type Query
, Typescript doesn't understand that the nested fields of AccessControlList.subscriberRel exist at "compile time". And the specialized generic type T
doesn't really extend the super type Query
.
Whenever I'm not using generics, I can use, say Extract<Query, ListCustomerTypesQuery>
to extract common properties but that doesn't make the error go away when I'm using generics Extract<Query, T>
.
I am able to get around this error by casting it as so, but I don't want to do this for ever single function call.
subscriber(result.value as ViewerType);
I would prefer to clean it up and keep it simply as
subscriber(result.value);
Query
? – Pliocene