Проблема с выводом Typescript

#reactjs #typescript

#reactjs #typescript

Вопрос:

Я пытаюсь создать универсальный магазин, используя useReducer и useContext от React, но у меня возникла проблема с выводом состояния по умолчанию.

Функция генератора хранилища выглядит следующим образом:

 export function generateStore<Actions extends ReducerAction, State = any>(defaultValue: State, reducer: (state: State, action: Actions) => State): {
  provider: (props: { children: ReactNode }) => ReactElement;
  dispatcher: (action: Actions) => void;
  useStore: () => State;
} {
  const store = createContext(defaultValue);
  const { Provider } = store;

  let dispatch: React.Dispatch<Actions>;

  const ProviderElm = (props: { children: ReactNode }): ReactElement => {
    const { children } = props;
    const [state, dispatcher] = useReducer(reducer, defaultValue);
    dispatch = dispatcher;
    return <Provider value={state}>{children}</Provider>;
  };

  return {
    provider: ProviderElm,
    dispatcher: (action: Actions) => dispatch amp;amp; dispatch(action),
    useStore: () => useContext(store),
  };
}
 

Примером инициализатора может быть:

 const defaultState = {
  auth: {
    authenticated: false,
  },
};

type StoreActions =
  | {
      type: 'LOGIN';
      payload: {
        token: string;
      };
    }
  | {
      type: 'LOGOUT';
    };

const { dispatcher, provider, useStore } = generateStore<StoreActions>(
  defaultState,
  (state = defaultState, action) => {
    switch (action.type) {
      case 'LOGIN': {
        const { token } = action.payload;
        return {
          ...state,
          auth: {
            authenticated: true,
            token,
          },
        };
      }
      case 'LOGOUT': {
        return {
          ...state,
          auth: {
            authenticated: false,
            token: null,
          },
        };
      }

      default:
        return defaultState;
    }
  },
);

 

Проблема в том, что State generic of generateStore не может вывести себя в качестве typeof параметра defaultValue .

Это всегда требует, чтобы я инициализировал его следующим образом, иначе intellisense не будет определять тип: generateStore<StoreActions, typeof defaultState>

Есть идеи о том, как я заставляю это работать и почему в настоящее время он не может определить тип?

Ответ №1:

Если вы хотите, чтобы TypeScript выводил ваши общие типы. Вы не можете предоставить функции какие-либо аргументы типа. TypeScript не поддерживает частичный вывод типа. Это все или ничего. Вызывая generateStore<StoreActions> , вы запускаете компилятор для использования предопределенного State = any универсального аргумента в вашей функции.

Я бы рекомендовал иметь строго типизированное состояние, чтобы сделать его более чистым.

 type State = {
  auth: {
    authenticated: boolean
  }
}

type StoreActions =
  | {
    type: 'LOGIN';
    payload: {
      token: string;
    };
  }
  | {
    type: 'LOGOUT';
  };

const defaultState: State = {
  auth: {
    authenticated: false,
  },
};

const { dispatcher, provider, useStore } = generateStore<StoreActions, State>(
  defaultState,
  (state = defaultState, action) => {
    switch (action.type) {
      case 'LOGIN': {
        const { token } = action.payload;
        return {
          ...state,
          auth: {
            authenticated: true,
            token,
          },
        };
      }
      case 'LOGOUT': {
        return {
          ...state,
          auth: {
            authenticated: false,
            token: null,
          },
        };
      }

      default:
        return defaultState;
    }
  },
);
 

Единственный другой вариант — создать функцию-оболочку, для вывода которой требуется только один аргумент (состояние) и которая напрямую предоставляет тип действий. Вам понадобится по одному для каждого набора действий, но это может быть хорошим решением в зависимости от того, сколько раз оно будет использоваться.

 type StoreActions =
  | {
    type: 'LOGIN';
    payload: {
      token: string;
    };
  }
  | {
    type: 'LOGOUT';
  };

const defaultState = {
  auth: {
    authenticated: false,
  },
};

export function generateStoreWithStoreActions<State = any>(defaultValue: State, reducer: (state: State, action: StoreActions) => State) {
  return generateStore<StoreActions, State>(defaultValue, reducer);
}

const { dispatcher, provider, useStore } = generateStoreWithStoreActions(
  defaultState,
  (state = defaultState, action) => {
    switch (action.type) {
      case 'LOGIN': {
        const { token } = action.payload;
        return {
          ...state,
          auth: {
            authenticated: true,
            token,
          },
        };
      }
      case 'LOGOUT': {
        return {
          ...state,
          auth: {
            authenticated: false,
            token: null,
          },
        };
      }

      default:
        return defaultState;
    }
  },
);
 

Комментарии:

1. Я понимаю, что вы имеете в виду. Идеальное объяснение. Позор. В любом случае у меня все еще есть обходной путь. Спасибо