Is your feature request related to a problem? Please describe.
Currently payload types generally consist of type unions, therefore it is not possible to take only one specific type and use it.
export type MyQuery {
__typename: 'Query',
data: Maybe<(
{
id: string;
name: string;
__typename: 'Data1'
}
| {
id: string;
type: string;
__typename: 'Data2'
}
)>;
}
Now if I develop a stream that filters out all emission but the one with Data2 __typename, I can't get the type safety right, as I have no direct access to the Data2 type.
Describe the solution you'd like
Similarly as apollo-codegen does it(_I used it up until now_) codegen could extract all the types in the unions in their own "standalone" types, that could be used by the consumer.
Describe alternatives you've considered
The only alternative is to declare the types by hand.
@klemenoslaj
You can use the Extract utility type (https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttu)
type Maybe<T> = null | undefined | T;
export type MyQuery = {
__typename: 'Query',
data: Maybe<(
{
id: string;
name: string;
bar: string;
__typename: 'Data1'
}
| {
id: string;
type: string;
foo: string;
__typename: 'Data2'
}
)>;
}
type MyQueryData1 = Extract<MyQuery["data"], { __typename: "Data1"}>
type MyQueryData2 = Extract<MyQuery["data"], { __typename: "Data2"}>
https://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAsghiARhAPAFQHxQLxQHYCuANkVAD5QF4AmEAZgJZ4TXlRoDcAUFxAB5gA9gCdgUUJFggAigQjCQOKFADeXZQH0NEiHjgBbCAC4oAclnyQpgDTqo1OMDgn4SVAAo7ytct9QG1CYAzsDCTADm3H7KeobBoRFR0YhwwvFheJFevlo6scZmACKOcACMptkAvtkUPtEB6YnZyjqNmUl+dIKCbVnRULngugYFpsVOAEwVftXKAJQY3NVcOlIWCuNlSgCifKFwAMbAKDAycgoA2gBEDk5XALrWqgPaQ-kmV5ulV5UYK0Nrc4gTYTHZ7YSHY6ndYga63OAPJ4qF55EYfEE-P5AA
Yes, this very second I found Extract myself 馃槀
Thanks for the quick reply.
Would however be splitting the types be good idea anyway?
@klemenoslaj you are using inline fragments? you can try to use fragments spreads and then it will generate a sub-type for each possible type in the fragment (use the latest alpha for this, it's just got merged and not released in a stable version yet).
@dotansimha not necessarily that fragments are used at all. It could be just a query itself that can return multiple data types.
Something like:
Schema
interface Data {
id: ID!
type: String!
}
type Data1 implements Data {
id: ID!
type: String!
foo: String!
}
type Data2 implements Data {
id: ID!
type: String!
boo: String!
}
extend type Query {
data(id: ID!): Data
}
Query
query DataQuery($id: ID!) {
data(id: $id) {
id
type
}
}
Type definition
export type DataQuery = { readonly __typename: 'Query' } & {
readonly data: Maybe<
| ({ readonly __typename: 'Data1' } & Pick<Data1, 'id' | 'type'>)
| ({ readonly __typename: 'Data2' } & Pick<Data2, 'id' | 'type'>)
>;
};
EDIT: In fact you're right, if fragment is not used in my example types will anyway be identical, therefore no need to pick one.
Bottomline currently solution to the problem would be to not use inline fragments, right?
I think it's fine, but if you'll use a separate fragment and a fragment spread you'll be able to access the inner type as regular typescript type.
And in case of inline fragment or fields specified for interface, you'll have to use Extract to access it (because we don't generate sub-type for that)
Most helpful comment
@klemenoslaj
You can use the
Extractutility type (https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttu)https://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAsghiARhAPAFQHxQLxQHYCuANkVAD5QF4AmEAZgJZ4TXlRoDcAUFxAB5gA9gCdgUUJFggAigQjCQOKFADeXZQH0NEiHjgBbCAC4oAclnyQpgDTqo1OMDgn4SVAAo7ytct9QG1CYAzsDCTADm3H7KeobBoRFR0YhwwvFheJFevlo6scZmACKOcACMptkAvtkUPtEB6YnZyjqNmUl+dIKCbVnRULngugYFpsVOAEwVftXKAJQY3NVcOlIWCuNlSgCifKFwAMbAKDAycgoA2gBEDk5XALrWqgPaQ-kmV5ulV5UYK0Nrc4gTYTHZ7YSHY6ndYga63OAPJ4qF55EYfEE-P5AA