-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreateAction.ts
139 lines (129 loc) · 5.27 KB
/
createAction.ts
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* Defines an action object with a type, data, and meta properties.
* @template Type - The type of the action.
* @template Data - The data payload of the action.
* @template Meta - The metadata of the action.
*/
type Action<Type extends string, Data = undefined, Meta = undefined> = Data extends undefined
? Meta extends undefined
? { type: Type }
: { type: Type; meta: Meta }
: Meta extends undefined
? { type: Type; data: Data }
: { type: Type; data: Data; meta: Meta };
/**
* Extender type for creating Redux actions with type, data, and meta properties.
* @template Type - The type of the action.
* @template Data - The data payload of the action.
* @template Meta - The meta data of the action.
* @property {string} type - The type of the action.
* @property {(action: Action<Type, Data, Meta>) => action is Action<Type, Data, Meta>} matches - A function that returns true if the action matches the type.
* @property {Action<Type, Data, Meta>} action - The action object.
*/
type Extender<Type extends string, Data = undefined, Meta = undefined> = Data extends undefined
? Meta extends undefined
? {
type: Type;
matches: (action: Action<Type>) => action is Action<Type>;
action: Action<Type>;
}
: {
type: Type;
matches: (action: Action<Type, undefined, Meta>) => action is Action<Type, undefined, Meta>;
action: Action<Type, undefined, Meta>;
}
: Meta extends undefined
? {
type: Type;
matches: (action: Action<Type, Data>) => action is Action<Type, Data>;
action: Action<Type, Data>;
}
: {
type: Type;
matches: (action: Action<Type, Data, Meta>) => action is Action<Type, Data, Meta>;
action: Action<Type, Data, Meta>;
};
/**
* Returns a function that creates an action object with the given type, data, and meta.
* @template Type - The type of the action.
* @template Data - The type of the data payload (optional).
* @template Meta - The type of the meta payload (optional).
* @param {Type} type - The type of the action.
* @param {Data} data - The data payload (optional).
* @param {Meta} meta - The meta payload (optional).
* @returns {ActionCreatorReturn<Type, Data, Meta>} - The action creator function.
*/
type ActionCreatorReturn<
Type extends string,
Data = undefined,
Meta = undefined
> = Data extends undefined
? Meta extends undefined
? (() => Action<Type>) & Extender<Type>
: ((meta: Meta) => Action<Type, undefined, Meta>) & Extender<Type, undefined, Meta>
: Meta extends undefined
? ((data: Data) => Action<Type, Data>) & Extender<Type, Data>
: ((data: Data, meta: Meta) => Action<Type, Data, Meta>) & Extender<Type, Data, Meta>;
/**
* Creates an action creator function that returns an action object with the given type, data, and meta.
* @template Type - The type of the action.
* @template Data - The type of the data payload (optional).
* @template Meta - The type of the meta payload (optional).
* @param {Type} type - The type of the action.
* @param {Data} data - The data payload (optional).
* @param {Meta} meta - The meta payload (optional).
* @returns {ActionCreatorReturn<Type, Data, Meta>} - The action creator function.
* @example
* const myAction1 = createAction('MY_ACTION_1');
* const myAction2 = createAction<string, string>('MY_ACTION_2');
* const myAction3 = createAction<string, number>('MY_ACTION_3');
*
* myAction1(); // { type: 'MY_ACTION_1' }
* myAction1.matches({ type: 'MY_ACTION_1' }); // true
* myAction1.matches({ type: 'MY_ACTION_2' }); // false
*
* myAction2('Hello'); // { type: 'MY_ACTION_2', data: 'Hello' }
* myAction2.matches({ type: 'MY_ACTION_2', data: 'Hello' }); // true
* myAction2.matches({ type: 'MY_ACTION_2', data: 123 }); // false
*
* myAction3('Hello', 123); // { type: 'MY_ACTION_3', data: 'Hello', meta: 123 }
* myAction3.matches({ type: 'MY_ACTION_3', data: 'Hello', meta: 123 }); // true
* myAction3.matches({ type: 'MY_ACTION_3', data: 'Hello', meta: '123' }); // false
*/
function createAction<Type extends string = string>(type: Type): ActionCreatorReturn<Type>;
function createAction<Data, Type extends string = string>(
type: Type
): ActionCreatorReturn<Type, Data>;
function createAction<Data, Meta, Type extends string = string>(
type: Type
): ActionCreatorReturn<Type, Data, Meta>;
function createAction<Data, Meta, Type extends string = string>(
type: Type
): ActionCreatorReturn<Type, Data, Meta> {
let _data: Data | undefined;
let _meta: Meta | undefined;
const caller = (data?: Data, meta?: Meta) => {
if (data && meta) {
_data = data;
_meta = meta;
return { type, data, meta };
} else if (data) {
_data = data;
return { type, data };
} else {
return { type };
}
};
caller.type = type;
caller.matches = (action: { type: string }): action is Action<Type, Data, Meta> =>
action.type === type;
if (typeof _data !== 'undefined' && typeof _meta !== 'undefined') {
caller.action = { type, data: _data, meta: _meta } as Action<Type, Data, Meta>;
} else if (typeof _data !== 'undefined') {
caller.action = { type, data: _data } as Action<Type, Data>;
} else {
caller.action = { type } as Action<Type>;
}
return caller as unknown as ActionCreatorReturn<Type, Data, Meta>;
}
export { createAction };