Home > Enterprise >  Is there any way to pass a generic when typing an object?
Is there any way to pass a generic when typing an object?

Time:10-05

First of all, I feel like I'm on the wrong path to begin with.

I have the following code:

export const fastMap = {};

export type FastMapOptions<S, T> = {
  key: string;
  value: T;
  selector: (state: RootState) => T;
  dispatch: (state: S, action: PayloadAction<any>) => void;
};

export const addToFastMap = <S, T>(options: FastMapOptions<S, T>) => {
  const { key, value, selector, dispatch } = options;

  fastMap[key] = {
    value,
    selector,
    atom: atom({ key, default: value }),
    dispatch
  };
};

As you can see, fastMap isn't really anything. However, my options (value, selector, atom, dispatch) are clearly typed. Unfortunately, I can't seem to get fastMap to play nice. If I were to do:

addToFastMap<SliceState, number>({
  key: "margin",
  value: 20,
  selector: (state: RootState) => state.properties.margin,
  dispatch: marginUpdatedReducer
});

And then simply access fastMap['margin'], it will not be able to tell me anything about what's in there, however, it's clear that the value is a number, the key is a string and so on.

Naturally, I'm using generics, as I need them here to be able to correctly type these arguments.

Is there any way to get the correct typings in this case?

CodePudding user response:

So you can achieve the desired behaviour by using a building pattern like this.


export type FastMapOptions<S, T> = {
    value: T;
    selector: (state: S) => T;
    dispatch: (state: S, action: PayloadAction<any>) => void;
};


function fastMapBuilder<T extends Record<string, any>>(inital: T) {
    const fastMap = inital
    const self = {
        fastMap,
        add<K extends string, State, Return>(key: K, options: FastMapOptions<State, Return>) {
            const next: T & { [Key in K]: { key: Key } & FastMapOptions<State, Return> } = { ...inital, [key]: { ...options, key } }
            return fastMapBuilder(next)
        },
        build() {
            return fastMap
        }
    }
    return self
}
const fastMap = fastMapBuilder({})
    .add("hover", { value: "idk", selector: (s: string) => "", dispatch: (s, a: {}) => { } })
    .add("over", { value: 0, selector: (s: string) => 0, dispatch: (s, a: {}) => { } }).build()

//{
//    hover: {
//        key: "hover";
//    } & FastMapOptions<string, string>;
//} & {
//    over: {
//        key: "over";
//    } & FastMapOptions<string, number>;
//}

playground

  • Related