Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Typescript] combineEvents forces invalid type #331

Open
5 of 14 tasks
Inlustra opened this issue Feb 6, 2019 · 1 comment
Open
5 of 14 tasks

[Typescript] combineEvents forces invalid type #331

Inlustra opened this issue Feb 6, 2019 · 1 comment

Comments

@Inlustra
Copy link

Inlustra commented Feb 6, 2019

This is a...

  • 🪲 Bug Report
  • 🚀 Feature Request
  • 📜 Documentation Request

Which version of Redux Beacon are you using?

  • v2.0.5

Which target(s) are you using?

  • Google Analytics
  • Google Analytics (gtag)
  • React Native Google Analytics
  • Google Tag Manager
  • React Native Google Tag Manager
  • Amplitude
  • Segment
  • Other/Third Party: ...(please specify here)

🪲 What are the steps to reproduce your issue?

  1. Create a custom target with action generics
  2. Use the custom target along with combineEvents and for example GoogleAnalytics gtag

🪲 What did you expect to happen?

I should be able to provide a custom type to the action.

🪲 What happened instead?

Typescript is unable to assign MyCustomAction to { [key:string]: any }, Example:

Argument of type 'EventDefinition<GenericEvent, SortByAction, any>' is not assignable to parameter of type 'EventDefinition<any, { [key: string]: any; }, any>'.
  Types of parameters 'action' and 'action' are incompatible.
    Type '{ [key: string]: any; }' is not assignable to type 'SortByAction'. [2345]```

😄 The larger picture

It looks as though GoogleAnalytics typescript types also override the Action in their EventDefinitions, therefore not allowing you to provide a custom type.

I think this needs to be addressed in a few places:

combineEvents:

Should become:

declare const combineEvents: (...eventDefs: EventDefinition<any, any, any>[]) => (action: any, prevState: any, nextState: any) => any[];

google-analytics-gtag/event-helpers

Should become:

export declare function trackPageView<A,S>(eventDef: EventDefinition<{
    title?: string;
    location?: string;
    path?: string;
}, A, S>, ...trackers: string[]) => EventDefinition<any, A, S>;
export declare function trackEvent<A, S>(eventDef: EventDefinition<{
    category: string;
    action: string;
    label?: string;
    value?: number;
}, A, S>): EventDefinition<any, A, S>;

perhaps, I'm wrong/missing something and perhaps this is all because of my setup - would be good to get some feedback 😄

Can you help out?

  • 🌟 I am a legend and can get started on a pull request right away given the go-ahead.
  • ⭐ I am a superstar and would like to help out given some guidance.
  • 😞 I won't be able to help out on this one.
@Inlustra
Copy link
Author

Inlustra commented Feb 6, 2019

I've spent longer on this than I probably should have, but should anyone be interested, I've created the following module to replace the types.

interface Actions<T extends string = string> {
  type: T;
}

declare module 'redux-beacon' {
  export declare type Target = (events: any[]) => void;
  export type EventDefinition<E = any, A extends Actions = any, S = any> = (
    action: A,
    prevState: S,
    nextState: S,
  ) => E;

  type FilterType<T, U> = T extends { type: infer S }
    ? U extends S
      ? T
      : never
    : never;

  type EventMap<A extends { type: string }, S = any> = Partial<
    { [K in A['type']]: EventDefinition<any, FilterType<A, K>, S> | undefined }
  >;
}

declare module '@redux-beacon/combine-events' {
  import { EventDefinition } from 'redux-beacon';

  declare function combineEvents<A extends Actions = any, S = any>(
    ...eventDefs: EventDefinition<any, A, S>[]
  ): (action: A, prevState: S, nextState: S) => any[];

  export default combineEvents;
}

declare module '@redux-beacon/google-analytics-gtag' {
  import { EventDefinition } from 'redux-beacon';
  export declare function trackPageView<A extends Actions = any, S = any>(
    eventDef: EventDefinition<
      {
        title?: string;
        location?: string;
        path?: string;
      },
      A,
      S
    >,
  ): EventDefinition<any, A, S>;

  export declare function trackEvent<A extends Actions = any, S = any>(
    eventDef: EventDefinition<
      {
        category: string;
        action: string;
        label?: string;
        value?: number;
      },
      A,
      S
    >,
  ): EventDefinition<any, A, S>;
}

The benefit of doing this, is that I can now write the following:

export const eventMap: EventMap<Action> = {
    [MY_CUSTOM_ACTION]: trackEvent(action => ({
      action: 'custom action' + action.payload.customProp,
      category: 'engagement',
    })
}

And all of the types are correctly inferred, action is inferred as MyCustomAction using the following module example:

export const MY_CUSTOM_ACTION = 'actionType1'
export type MyCustomAction {
    type: typeof MY_CUSTOM_ACTION,
    payload: {
        customProp: number
    }
} 

export type Action = MyCustomAction | MyCustomAction2... etc

It just means that everything is typed correctly and VSCode will give you some nice help when working out which action type to accept

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant