11import { Analytics , AnalyticsBrowser } from '@segment/analytics-next'
22import React , {
3- FunctionComponent ,
4- ReactElement ,
3+ ReactNode ,
54 createContext ,
65 useContext ,
76 useEffect ,
87 useMemo ,
98 useState ,
109} from 'react'
1110
12- interface SegmentContextInteface {
11+ type EventFunction = ( ...args : unknown [ ] ) => Promise < void >
12+ type Events = Record < string , ( analytics ?: Analytics ) => EventFunction >
13+
14+ interface SegmentContextInterface < T extends Events = Events > {
1315 analytics : Analytics | undefined
14- events : Record < string , unknown >
16+ events : { [ K in keyof T ] : ReturnType < T [ K ] > }
1517 writeKey ?: string
1618 onError ?: ( err : Error ) => void
1719}
@@ -23,29 +25,32 @@ const initialContext = {
2325 writeKey : undefined ,
2426}
2527
26- const SegmentContext = createContext < SegmentContextInteface > ( initialContext )
28+ const SegmentContext = createContext < SegmentContextInterface > ( initialContext )
2729
28- export const useSegment = ( ) : SegmentContextInteface => {
29- const context = useContext ( SegmentContext )
30+ export function useSegment < T extends Events > ( ) : SegmentContextInterface < T > {
31+ // @ts -expect-error Here we force cast the generic onto the useContext because the context is a
32+ // global variable and cannot be generic
33+ const context = useContext < SegmentContextInterface < T > > ( SegmentContext )
3034 if ( context === undefined ) {
3135 throw new Error ( 'useSegment must be used within a SegmentProvider' )
3236 }
3337
3438 return context
3539}
3640
37- export type SegmentProviderProps = {
41+ export type SegmentProviderProps < T > = {
3842 writeKey ?: string
3943 onError : ( err : Error ) => void
40- events : Record < string , unknown >
44+ events : T
45+ children : ReactNode
4146}
4247
43- const SegmentProvider : FunctionComponent < SegmentProviderProps > = ( {
48+ function SegmentProvider < T extends Events > ( {
4449 children,
4550 writeKey,
4651 onError,
4752 events,
48- } ) : ReactElement => {
53+ } : SegmentProviderProps < T > ) {
4954 const [ analytics , setAnalytics ] = useState < Analytics | undefined > ( undefined )
5055
5156 useEffect ( ( ) => {
@@ -63,13 +68,20 @@ const SegmentProvider: FunctionComponent<SegmentProviderProps> = ({
6368 }
6469 } , [ onError , writeKey ] )
6570
66- const value = useMemo < SegmentContextInteface > (
67- ( ) => ( {
71+ const value = useMemo < SegmentContextInterface < T > > ( ( ) => {
72+ const curiedEvents = Object . entries ( events ) . reduce (
73+ ( acc , [ eventName , eventFn ] ) => ( {
74+ ...acc ,
75+ [ eventName ] : eventFn ( analytics ) ,
76+ } ) ,
77+ { } ,
78+ ) as { [ K in keyof T ] : ReturnType < T [ K ] > }
79+
80+ return {
6881 analytics,
69- events,
70- } ) ,
71- [ analytics , events ] ,
72- )
82+ events : curiedEvents ,
83+ }
84+ } , [ analytics , events ] )
7385
7486 return (
7587 < SegmentContext . Provider value = { value } > { children } </ SegmentContext . Provider >
0 commit comments