aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

Export generic type NeverEmpty<T> from api-graphql #12968

Open timheilman opened 7 months ago

timheilman commented 7 months ago

Is this related to a new or existing framework?

No response

Is this related to a new or existing API?

GraphQL API

Is this related to another service?

No response

Describe the feature you'd like to request

Feature I’d like to request:

I would like to be able to build upon the GraphQL Subscription pattern provided by amplify in a type-safe way. In particular, in this code snippet:

const sub = client
  .graphql({
    query: subscriptions.onCreateTodo,
    variables
  })
  .subscribe({
    next: ({ data }) => typeSafeHandler(data.onCreateTodo),
    error: (error) => console.warn(error)
  });

I would like to be able to handle errors in a uniform manner, while passing the query, variables, and typeSafeHandler items into a method such as this:

// amplify CLI generates a Subscriptions type, and this:
export type GeneratedSubscription<InputType, OutputType> = string & {
  __generatedSubscriptionInput: InputType;
  __generatedSubscriptionOutput: OutputType;
};

export type SubscriptionNames = keyof Omit<Subscription, "__typename">;

// I like to have access to the name of the subscription:
export interface KeyedGeneratedSubscription<
  NAME extends SubscriptionNames,
  ARGS,
> {
  gql: GeneratedSubscription<ARGS, Pick<Subscription, NAME>>;
  __subscriptionName: NAME;
}

// Here’s where the NeverEmpty issue occurs:
export type CallbackParamType<T> = T extends KeyedGeneratedSubscription<
  infer NAME,
  unknown
>
  ? NeverEmpty<Pick<Subscription, NAME>>[NAME]
  : never;

// so that I can have this signature for the function that handles errors uniformly:
useSubscription<NAME extends SubscriptionNames, InputType>({
  query: KeyedGeneratedSubscription<NAME, InputType>,
  variables: InputType,
  typeSafeHandler: (result: CallbackParamType<KeyedGeneratedSubscription<NAME, InputType>>) => void
});

I have this working, but it requires me to copy the source code for the type definitions for NeverEmpty, WithListsFixed, and PagedList out of api-graphql/types. That gives me a bad feeling, and I’d prefer instead to do some “right” thing.

Describe the solution you'd like

I would like to go ahead and publicly export the NeverEmpty type, so that I can use it in the above code without having to copy-and-paste source code out of the @aws-amplify/api-graphql repo into my own repo, in order to be able to specify a wrapping signature for the subscribe method that handles errors uniformly and unwraps the successful notifications' results before passing them to a type-safe handler.

Describe alternatives you've considered

It would be ideal if these types did not need to be exported, and I could nonetheless arrange something using the tools ReturnType, Parameters, typeof, and the [] types operator, as described in this stackoverflow question.

However, there are two issues prevent this. First, the rxjs library happens to define a deprecated overload of the subscribe function after the one I want. Inferring the parameters’ types is going to provide this second overload’s parameters, eclipsing the first overload’s parameters I’m actually interested in. Second, that first subscribe’s definition’s parameter is a union type, but I would like to restrict only to the first part of the union. Without NeverEmpty to begin with, I cannot use Exclude to select only the first half of that union type.

Additional context

No response

Is this something that you'd be interested in working on?

nadetastic commented 7 months ago

HI @timheilman thank you for opening this feature request and for providing the context. I will discuss this with the team and will follow up soon. In the meantime, let me know if you have any questions.

timheilman commented 2 months ago

Sure enough, it was a dicey decision to copy in source code that wasn't exported. This commit caused type breakage.