Issue
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."
Solution
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.
To help TypeScript automatically infer the type of T, you need to also change the defintion of createActions.
export const createActions = <T extends ActionsMapConfigType>(prefix: string, actionsMap: T): ActionMapType<T> => {
/* ... */
};
Answered By - Tobias S.
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.