How i can set correct type for config
and don't lose keys for generic below?
Types:
type ActionCreatorType = (...args: any[]) => any;
type ActionConfig = [ActionCreatorType, ActionCreatorType]
type ActionsMapConfigType = Record<string, ActionConfig>
type ActionMapType<T extends ActionsMapConfigType> = {
[K in keyof T]: ActionCreator<T[K][0], T[K][1]>
}
type ActionType<P extends ActionCreatorType, M extends ActionCreatorType> = {
type: string,
payload: P,
meta: M,
}
type ConcatArguments<P extends ActionCreatorType, M extends ActionCreatorType> = Parameters<P> & Parameters<M>;
type ActionCreator<P extends ActionCreatorType, M extends ActionCreatorType> = (...args: ConcatArguments<P, M>) => ActionType<P, M>
utils:
export const createActions = <T extends ActionsMapConfigType>(prefix: string, actionsMap: ActionsMapConfigType): ActionMapType<T> => {
const result: Partial<ActionMapType<T>> = {};
for (const [key, value] of Object.entries(actionsMap)) {
result[key as keyof ActionMapType<T>] = (...args: any[]) => ({
type: `${prefix}_${key}`,
payload: value[0](...args),
meta: Object.assign({}, ...value[1](...args)),
})
}
return result as ActionMapType<T>;
};
using
const config = {
test: [
(a: number) => ({}),
(a: number, b: string) => ({}),
]
}
const action = createActions<typeof config>('form', config);
createActions<typeof config>('form', config)
- contain error "Target requires 2 element(s) but source may have fewer."
CodePudding user response:
The problem here is the type widening of the config
object. When you hover over config
, you will see that the type is
const config: {
test: ((a: number, b: string) => {})[];
}
instead of
const config: {
test: [
(a: number) => {},
(a: number, b: string) => {}
]
}
So TypeScript automatically widened the type of config
to contain an array instead of a tuple of length 2
. The information about the amount of elements inside the array was lost which logically leads to the error message.
Target requires 2 element(s) but source may have fewer.
What can we do to fix this?
We could use the as const
assetions when declaring config
.
const config = {
test: [
(a: number) => ({}),
(a: number, b: string) => ({}),
] as const
}
This will force TypeScript to use the narrowest type. In this case a tuple.
It marks the tuple as readonly
though so we have to modify ActionConfig
to accept readonly
tuples.
type ActionConfig = readonly [ActionCreatorType, ActionCreatorType]
The error is now gone.
You can also directly pass the object literal to the createActions
function.
const action = createActions('form', {
test: [
(a: number) => ({}),
(a: number, b: string) => ({}),
]
});
The type will be narrowed down correctly here too.