简体   繁体   中英

Redux Toolkit in TypeScript: createAction abstraction of adding timestamp

Using Redux Toolkit with TypeScript, I want to be able to add a timestamp property to all payloads using a prepare callback on createAction() and maintain good type definitions. Currently I do this with a lot of repeated code like:

const timestampAdder = (unpreparedPayload) => {
    const prepared = { payload: unpreparedPayload };
    if (!unpreparedPayload.timestamp) {
        prepared.payload.timestamp = Date.now();
    }
    return prepared;
};

export const someActionCreator = createAction(
  'slice_name/action_name',
  (
    prePayload: Omit<MyPayloadInterface, 'timestamp'>
  ): { payload: MyPayloadInterface; } => timestampAdder(prePayload)
);

export const someActionCreator2 = createAction(
  'slice_name/action2_name',
  (
    prePayload: Omit<EntirelyDifferentInterface, 'timestamp'>
  ): { payload: EntirelyDifferentInterface; } => timestampAdder(prePayload)
);

// ...repeat the same structure for each action I want to add "timestamp" to...

If this was plain JS (or less strict TS) I could do something like:

export const jsActionCreator1 = createAction('js/action_name1', timestampAdder);
export const jsActionCreator2 = createAction('js/action_name2', timestampAdder);

But because the interfaces for the various action creators in TS are different, I end up writing a lot of boilerplate for each to maintain good types. I could do the same in TS:

export const myActionCreator = createAction('slice_name/action3_name', timestampAdder);

But then I lose the benefit of types, and I can invoke the action creator with any argument:

myActionCreator(123);
myActionCreator('abc');
myActionCreator({ a: 1, b: 'c' });

Is there a good way to reduce boilerplate but maintain good types? Thanks!

Just do something like

const timestampAdder = <T>() => (unpreparedPayload: T) => {
    const payload = Object.assign( { timestamp: Date.now() }, unpreparedPayload )
    return { payload };
};

and use it like

export const actionCreator1 = createAction('js/action_name1', timestampAdder<{foo: string}>());
export const actionCreator2 = createAction('js/action_name2', timestampAdder<{bar: number}>());

Essentially, you have one method call wrapped around, just to "bind" the type there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM