File size: 2,591 Bytes
c2b7eb3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import type { Action, Middleware, UnknownAction } from 'redux'
import type { ThunkDispatch } from 'redux-thunk'
import { configureStore } from '../../configureStore'
import { createDynamicMiddleware } from '../index'

const untypedInstance = createDynamicMiddleware()

interface AppDispatch extends ThunkDispatch<number, undefined, UnknownAction> {
  (n: 1): 1
}

const typedInstance = createDynamicMiddleware<number, AppDispatch>()

declare const staticMiddleware: Middleware<(n: 1) => 1>

const store = configureStore({
  reducer: () => 0,
  middleware: (gDM) =>
    gDM().prepend(typedInstance.middleware).concat(staticMiddleware),
})

declare const compatibleMiddleware: Middleware<{}, number, AppDispatch>
declare const incompatibleMiddleware: Middleware<{}, string, AppDispatch>

declare const addedMiddleware: Middleware<(n: 2) => 2>

describe('type tests', () => {
  test('instance typed at creation ensures middleware compatibility with store', () => {
    const store = configureStore({
      reducer: () => '',
      // @ts-expect-error
      middleware: (gDM) => gDM().prepend(typedInstance.middleware),
    })
  })

  test('instance typed at creation enforces correct middleware type', () => {
    typedInstance.addMiddleware(
      compatibleMiddleware,
      // @ts-expect-error
      incompatibleMiddleware,
    )

    const dispatch = store.dispatch(
      typedInstance.withMiddleware(
        compatibleMiddleware,
        // @ts-expect-error
        incompatibleMiddleware,
      ),
    )
  })

  test('withTypes() enforces correct middleware type', () => {
    const addMiddleware = untypedInstance.addMiddleware.withTypes<{
      state: number
      dispatch: AppDispatch
    }>()

    addMiddleware(
      compatibleMiddleware,
      // @ts-expect-error
      incompatibleMiddleware,
    )

    const withMiddleware = untypedInstance.withMiddleware.withTypes<{
      state: number
      dispatch: AppDispatch
    }>()

    const dispatch = store.dispatch(
      withMiddleware(
        compatibleMiddleware,
        // @ts-expect-error
        incompatibleMiddleware,
      ),
    )
  })

  test('withMiddleware returns typed dispatch, with any applicable extensions', () => {
    const dispatch = store.dispatch(
      typedInstance.withMiddleware(addedMiddleware),
    )

    // standard
    expectTypeOf(dispatch({ type: 'foo' })).toEqualTypeOf<Action<string>>()

    // thunk
    expectTypeOf(dispatch(() => 'foo')).toBeString()

    // static
    expectTypeOf(dispatch(1)).toEqualTypeOf<1>()

    // added
    expectTypeOf(dispatch(2)).toEqualTypeOf<2>()
  })
})