Home > OS >  how to get return type of map & only give S
how to get return type of map & only give S

Time:02-25

how to get return type of map & only give S.
how to get return type of map & only give S.
how to get return type of map & only give S.
how to get return type of map & only give S.
how to get return type of map & only give S.


const state = {
  a: 1,
  b: 2
}

type StateValue = typeof state

type Getter<S, R, R1> = {
  map: (state: S) => R
  mapB?: (state: S) => R1
  // more ...
}

function getter<S extends Getter<any, any, any>>(
  a: S
): ReturnType<S['map']> & ReturnType<S['mapB']> {
  return {
    ...a.map(state),
    ...a.mapB?.(state)
    // more ...
  }
}

// to much type
const v = getter({
  map: (state: StateValue) => ({
    a: state.a
  }),
  mapB: (state: StateValue) => ({
    b: state.b
  })
  // more map ...
})

// want!! // state will be <{ a: 1, b: 2, c: 1 }>
const v = getter<StateValue>({
  map: (state) => ({
    a: state.a,
    c: state.a
  }),
  mapB: (state) => ({
    b: state.b
  })
})

CodePudding user response:

Edit: Updated again in response to updated question

You should accept the state as an additional parameter to getter, rather than relying on closing over it:

TS Playground

type Fn<Params extends readonly any[] = readonly any[], Result = any> =
  (...params: Params) => Result;

type FnMap<S> = Record<string, Fn<[state: S]>>;

declare function getter<S, T extends FnMap<S>>(state: S, map: T): {
  [K in keyof T]: ReturnType<T[K]>;
};

const state = { a: 1, b: 2 };
type State = typeof state;

const functions = {
  mapA: (state: State) => ({ a: state.a }),
  mapB: (state: State) => ({ b: state.b }),
};

const v = getter(state, functions);

v; // { mapA: {a: number}; mapB: {b: number}; }


Edit: Updated in response to updated question

TS Playground

type Fn<Params extends readonly any[] = readonly any[], Result = any> =
  (...params: Params) => Result;

type FnMap = Record<string, Fn>;

declare function getter<T extends FnMap, K extends keyof T>(map: T, key: K): ReturnType<T[K]>;

const functions = {
  map1: (state: { a: number }) => ({ b: state.a }),
  map2: (state: { b: string }) => ({ c: state.b }),
};

const v = getter(functions, 'map1');

v; // { b: number }

You can do it completely with inference. Just move the generic type you were supplying for S from getter to the state parameter in your map method:

TS Playground

type Getter<S, R> = {
  map: (state: S) => R;
};

declare function getter<T extends Getter<any, any>>(value: T): ReturnType<T['map']>;

const v = getter({
  map: (state: { a: number }) => ({ b: state.a }),
});

v; // { b: number }
  • Related