машинопись, как набрать прокси, который карри его реквизит и ввести реквизит - PullRequest
0 голосов
/ 28 октября 2019

У меня есть вспомогательная функция, которая принимает и Object из функций и возвращает прокси этого объекта с его функциями, составленными с введенным первым аргументом.

лучше показано в коде: -

// example of object
export const selectors = {
  selectA: (state:State) => state.a,
  addToA : (state:State, num:number) => state.a + num
}

// ... in another file my helper fn

import {useSelector} from 'react-redux';

// PROBLEM IS HERE...
export const function bindSelector(obj){
  return new Proxy(obj,{
    get: (main, key, ctx) => {
      const fn = main[key].bind(ctx);

      // always inject store as first prop of fn
      // we get store arg from useSelector higher order (its already properly typed)
      return props => useSelector( store=> fn(store, props) )
    }

  })
}

export default bindSelector(selectors);

поэтому я использую прокси, так что мне не нужно включать useSelector или передавать параметры хранилища каждому селектору, когда я его использую

пример использования этого будет

import selectors from './a';

// now i can just
const a = selectors.selectA(); // give error param missing
const aAnd2 = selectors.addToA(2); // how to tell typescript that this first arg is in fact the 2nd arg :D !.

Проблема

  1. если я ввожу bindSelector для возврата того же типа (obj), то получаю ошибку selecte require 1 param, потому что машинопись не знает, что мой прокси уже предоставил первый параметр функции.

1 Ответ

1 голос
/ 28 октября 2019

Вы можете использовать некоторые сопоставленные и условные типы для сопоставления исходного объекта с новым объектом, где у каждой функции есть на один аргумент меньше:


import { useSelector } from 'react-redux';

type State = {
  a: number
}
// example of object
export const selectors = {
  selectA: (state: State) => state.a,
  addToA: (state: State, num: number) => state.a + num
}

type CurriedFunctionObject<T> = {
  [P in keyof T]: T[P] extends (s: State, ...p: infer P) => infer R ? (...p: P) => R : never
}

export function bindSelector<T extends Record<string, (...a: any) => any>>(obj: T): CurriedFunctionObject<T> {
  return new Proxy(obj, {
    get: (main, key, ctx) => {
      const fn = main[key as keyof T].bind(ctx);

      // always inject store as first prop of fn
      // we get store arg from useSelector higher order (its already properly typed)
      return (args: any[]) => useSelector(store => fn(store, ...args))
    }

  }) as any as CurriedFunctionObject<T>
}

const cSelectors = bindSelector(selectors);
const a = cSelectors.selectA(); // give error param missing
const aAnd2 = cSelectors.addToA(2)

Воспроизведение

Вы можете заменить State на любой условный тип, чтобы тип работал для любого состояния.

...