Reputation: 6027
Typescript: 2.4.1
I'm attempting to create a helper function to generate redux action creators.
What I've got:
interface IAction<T extends string, P = undefined> {
type: T;
payload: P;
}
function createAction<T extends string>(type: T): () => IAction<T, undefined>;
function createAction<T extends string = '', U = undefined, V = {}>(type: T, actionCreator: (payload: U) => V): (payload: U) => IAction<T, V>;
function createAction<T extends string>(type: T, actionCreator?: (payload?: any) => any) {
return (payload: any) => ({
payload,
type,
});
}
enum ActionType {
INCREMENT = 'COUNTER/INCREMENT',
DECREMENT = 'COUNTER/DECREMENT',
ASYNC_INCREMENT = 'COUNTER/ASYNC_INCREMENT',
}
const increment = createAction(ActionType.INCREMENT, () => ({ amount: 1 }));
const incrementBy = createAction(ActionType.INCREMENT, (amount: number) => amount);
In this example, I expect increment
to be a function that takes 0 arguments, and incrementBy
to take exactly 1 (a number
).
incrementBy
works as expected, but Typescript throws this error when calling increment()
(with no arguments):
[ts] Expected 1 arguments, but got 0;
Trying to call it the same way I call incrementBy
, e.g. increment(42)
, I get this error:
[ts] Argument of type '42' is not assignable to parameter of type 'undefined';
This works: increment(undefined)
.
Is there a way to fix this?
Upvotes: 3
Views: 1876
Reputation: 27377
You should simply create another overload which says it'll return a function with no arguments, rather than a function which takes undefined
as the only argument.
function createAction<T extends string>(type: T): () => IAction<T, undefined>;
function createAction<T extends string, V>(type: T, actionCreator: () => V): () => IAction<T, V>;
function createAction<T extends string, U, V>(type: T, actionCreator: (payload: U) => V): (payload: U) => IAction<T, V>;
function createAction<T extends string, U, V>(type: T, actionCreator?: (payload: U) => V): (payload: U) => IAction<T, V> {
return (payload: any) => ({
payload,
type,
});
}
However, your implementation doesn't seem to do anything with the function being passed. You likely want to do this:
function createAction<T extends string>(type: T): () => IAction<T, undefined>;
function createAction<T extends string, V>(type: T, actionCreator: () => V): () => IAction<T, V>;
function createAction<T extends string, U, V>(type: T, actionCreator: (payload: U) => V): (payload: U) => IAction<T, V>;
function createAction<T extends string, U, V>(type: T, actionCreator?: (payload: U) => V): (payload: U) => IAction<T, V> {
if (actionCreator) {
return (payload: U) => ({
payload: actionCreator(payload),
type,
});
} else {
return () => ({
payload: undefined,
type
});
}
}
Upvotes: 2