import type { Action, AnyAction, ActionCreator } from 'redux' import type { PayloadAction, PayloadActionCreator, ActionCreatorWithoutPayload, ActionCreatorWithOptionalPayload, ActionCreatorWithPayload, ActionCreatorWithNonInferrablePayload, ActionCreatorWithPreparedPayload, } from '@reduxjs/toolkit' import { createAction } from '@reduxjs/toolkit' import type { IsAny } from '@internal/tsHelpers' import { expectType } from './helpers' /* PayloadAction */ /* * Test: PayloadAction has type parameter for the payload. */ { const action: PayloadAction = { type: '', payload: 5 } const numberPayload: number = action.payload // @ts-expect-error const stringPayload: string = action.payload } /* * Test: PayloadAction type parameter is required. */ { // @ts-expect-error const action: PayloadAction = { type: '', payload: 5 } // @ts-expect-error const numberPayload: number = action.payload // @ts-expect-error const stringPayload: string = action.payload } /* * Test: PayloadAction has a string type tag. */ { const action: PayloadAction = { type: '', payload: 5 } // @ts-expect-error const action2: PayloadAction = { type: 1, payload: 5 } } /* * Test: PayloadAction is compatible with Action */ { const action: PayloadAction = { type: '', payload: 5 } const stringAction: Action = action } /* PayloadActionCreator */ /* * Test: PayloadActionCreator returns correctly typed PayloadAction depending * on whether a payload is passed. */ { const actionCreator = Object.assign( (payload?: number) => ({ type: 'action', payload, }), { type: 'action' } ) as PayloadActionCreator expectType>(actionCreator(1)) expectType>(actionCreator()) expectType>(actionCreator(undefined)) // @ts-expect-error expectType>(actionCreator()) // @ts-expect-error expectType>(actionCreator(1)) } /* * Test: PayloadActionCreator is compatible with ActionCreator. */ { const payloadActionCreator = Object.assign( (payload?: number) => ({ type: 'action', payload, }), { type: 'action' } ) as PayloadActionCreator const actionCreator: ActionCreator = payloadActionCreator const payloadActionCreator2 = Object.assign( (payload?: number) => ({ type: 'action', payload: payload || 1, }), { type: 'action' } ) as PayloadActionCreator const actionCreator2: ActionCreator> = payloadActionCreator2 } /* createAction() */ /* * Test: createAction() has type parameter for the action payload. */ { const increment = createAction('increment') const n: number = increment(1).payload // @ts-expect-error increment('').payload } /* * Test: createAction() type parameter is required, not inferred (defaults to `void`). */ { const increment = createAction('increment') // @ts-expect-error const n: number = increment(1).payload } /* * Test: createAction().type is a string literal. */ { const increment = createAction('increment') const n: string = increment(1).type const s: 'increment' = increment(1).type // @ts-expect-error const r: 'other' = increment(1).type // @ts-expect-error const q: number = increment(1).type } /* * Test: type still present when using prepareAction */ { const strLenAction = createAction('strLen', (payload: string) => ({ payload: payload.length, })) expectType(strLenAction('test').type) } /* * Test: changing payload type with prepareAction */ { const strLenAction = createAction('strLen', (payload: string) => ({ payload: payload.length, })) expectType(strLenAction('test').payload) // @ts-expect-error expectType(strLenAction('test').payload) // @ts-expect-error const error: any = strLenAction('test').error } /* * Test: adding metadata with prepareAction */ { const strLenMetaAction = createAction('strLenMeta', (payload: string) => ({ payload, meta: payload.length, })) expectType(strLenMetaAction('test').meta) // @ts-expect-error expectType(strLenMetaAction('test').meta) // @ts-expect-error const error: any = strLenMetaAction('test').error } /* * Test: adding boolean error with prepareAction */ { const boolErrorAction = createAction('boolError', (payload: string) => ({ payload, error: true, })) expectType(boolErrorAction('test').error) // @ts-expect-error expectType(boolErrorAction('test').error) } /* * Test: adding string error with prepareAction */ { const strErrorAction = createAction('strError', (payload: string) => ({ payload, error: 'this is an error', })) expectType(strErrorAction('test').error) // @ts-expect-error expectType(strErrorAction('test').error) } /* * regression test for https://github.com/reduxjs/redux-toolkit/issues/214 */ { const action = createAction<{ input?: string }>('ACTION') const t: string | undefined = action({ input: '' }).payload.input // @ts-expect-error const u: number = action({ input: '' }).payload.input // @ts-expect-error const v: number = action({ input: 3 }).payload.input } /* * regression test for https://github.com/reduxjs/redux-toolkit/issues/224 */ { const oops = createAction('oops', (x: any) => ({ payload: x, error: x, meta: x, })) type Ret = ReturnType const payload: IsAny = true const error: IsAny = true const meta: IsAny = true // @ts-expect-error const payloadNotAny: IsAny = false // @ts-expect-error const errorNotAny: IsAny = false // @ts-expect-error const metaNotAny: IsAny = false } /** * Test: createAction.match() */ { // simple use case { const actionCreator = createAction('test') const x: Action = {} as any if (actionCreator.match(x)) { expectType<'test'>(x.type) expectType(x.payload) } else { // @ts-expect-error expectType<'test'>(x.type) // @ts-expect-error expectType(x.payload) } } // special case: optional argument { const actionCreator = createAction('test') const x: Action = {} as any if (actionCreator.match(x)) { expectType<'test'>(x.type) expectType(x.payload) } } // special case: without argument { const actionCreator = createAction('test') const x: Action = {} as any if (actionCreator.match(x)) { expectType<'test'>(x.type) // @ts-expect-error expectType<{}>(x.payload) } } // special case: with prepareAction { const actionCreator = createAction('test', () => ({ payload: '', meta: '', error: false, })) const x: Action = {} as any if (actionCreator.match(x)) { expectType<'test'>(x.type) expectType(x.payload) expectType(x.meta) expectType(x.error) // @ts-expect-error expectType(x.payload) // @ts-expect-error expectType(x.meta) // @ts-expect-error expectType(x.error) } } // potential use: as array filter { const actionCreator = createAction('test') const x: Array> = [] expectType>>( x.filter(actionCreator.match) ) expectType>>( // @ts-expect-error x.filter(actionCreator.match) ) } } { expectType>( createAction('') ) expectType(createAction('')) expectType(createAction('')) expectType>(createAction('')) expectType>( createAction('', (_: 0) => ({ payload: 1 as 1, error: 2 as 2, meta: 3 as 3, })) ) const anyCreator = createAction('') expectType>(anyCreator) type AnyPayload = ReturnType['payload'] expectType>(true) }