= P extends []
? []
: P extends [p?: infer TPayload]
? P extends [infer TPayloadMayUndefined]
? [p: TPayloadMayUndefined]
: [p?: TPayload]
: P extends [p?: infer TPayload, m?: infer TMeta, ...args: unknown[]]
? P extends [
infer TPayloadMayUndefined,
infer TMetaMayUndefined,
...unknown[]
]
? [p: TPayloadMayUndefined, m: TMetaMayUndefined]
: P extends [infer TPayloadMayUndefined, unknown?, ...unknown[]]
? [p: TPayloadMayUndefined, m?: TMeta]
: [p?: TPayload, m?: TMeta]
: [];
```
Careful readers can see that in `ExtractParametersFromReducer`, in addition to `infer TPayload`, I also have `infer TPayloadMayUndefined`. This is because when a user defines a parameter as `p: number | undefined`, the `TPayload` type only has `number`, and `undefined` is ignored. Therefore, here we use `TPayloadMayUndefined` to correctly deduce this situation. (The same applies to `TMetaMayUndefined` below.)
### **Extracting Parameters from `Effect`**
In Rematch's `effects`, the second parameter `rootState` is the one we need to ignore. Since it's not convenient to directly remove the second parameter type, we pass all the parameters to the corresponding `ExtractParametersFromEffect` for processing. The code is as follows:
```ts
// Due to the inconvenience of directly removing the second parameter, all parameters are extracted and handed over to ExtractParametersFromEffect for processing.
export type ExtractRematchDispatcherFromEffect<
TEffect extends ModelEffect

= P extends [] ? [] : P extends [p?: infer TPayload, s?: unknown] ? P extends [infer TPayloadMayUndefined, ...unknown[]] ? [p: TPayloadMayUndefined] : [p?: TPayload] : P extends [ p?: infer TPayload, s?: unknown, m?: infer TMeta, ...args: unknown[] ] ? P extends [ infer TPayloadMayUndefined, unknown, infer TMetaMayUndefined, ...unknown[] ] ? [p: TPayloadMayUndefined, m: TMetaMayUndefined] : P extends [infer TPayloadMayUndefined, unknown?, unknown?, ...unknown[]] ? [p: TPayloadMayUndefined, m?: TMeta] : [p?: TPayload, m?: TMeta] : []; ``` The situation with `effect` is roughly similar to `reducer`. In cases containing only `payload`, we define the second parameter `s` as optional in the condition, so it can hit this branch whether it's defined or not. Then, for situations with three or more parameters, we first judge for mandatory and finally for optional, consistent with `reducer`. > When `effect` was initially designed, considering that `payload` is a frequently used parameter, it was placed first, with `rootState` in the second position. Subsequently added `meta` was naturally put in the third position, and `meta` is less frequently used. For `reducer`, the current `model`'s `state` generally has a higher frequency of access, so it's placed first, while `payload` and `meta` are in the second and third positions, respectively. ## Conclusion When dealing with such gymnastics problems, it's important to first think about whether there are constraints that can gradually narrow down the possible range. For example, by the rule that "arrays with more parameters cannot be assigned to those with fewer," we can use the constraint of "arrays with fewer parameters" to first handle cases with fewer parameters, and then gradually deal with cases with more parameters. Within each case, we continue to use the rule that "arrays with optional parameters cannot be assigned to those with mandatory parameters," using the constraint of "arrays with mandatory parameters" to first handle mandatory parameter situations. Also, for multiple parameters, since [TS requires optional parameters to be placed after mandatory ones](https://stackoverflow.com/q/46958782/14251417), we prioritize handling the situation where the optional parameters are at the end, and finally deal with the situation where all parameters are optional. Once you find such patterns, you are a TypeScript magician🏆!