Home > other >  Target requires 2 element(s) but source may have fewer
Target requires 2 element(s) but source may have fewer

Time:06-12

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.

Playground


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.

  • Related