강의

멘토링

커뮤니티

Cộng đồng Hỏi & Đáp của Inflearn

Hình ảnh hồ sơ của rmh03130151
rmh03130151

câu hỏi đã được viết

Ứng dụng TypeScript vào React đã học khi làm web game

useReducer 사용시 dispatch 함수에 generic으로 action 타입을 넘겨줄 때 에러가 발생합니다.

Viết

·

739

0

제로초님 안녕하세요. 

TicTacToe 코드에서 질문이 있습니다. 

const TicTacToe = () => {
  const [state, dispatch] = useReducer<Reducer<ReducerState, ReducerActions>>(reducer, initialState);
  ... 
  if (win) {
    dispatch<SetWinnerAction>({ type: SET_WINNER, winner: turn });
    dispatch({ type: RESET_GAME});
  } ...
}

 위의 코드에서 SET_WINNER action을 dispatch 하는 부분에서처럼 dispatch에 generic으로 SetWinnerAction 인터페이스를 줘서 표기를 확실히하고 싶은데, 저렇게 하면 SetWinnerAction 부분에서 타입에러가 나면서 "Expected 0 arguments, but got 1" 이라는 메시지가 뜹니다. 현재 웹스톰 사용 중인데, type definition을 따라가보면 useReducer가 

function useReducer<R extends ReducerWithoutAction<any>, I>(
reducer: R,
initializerArg: I,
initializer: (arg: I) => ReducerStateWithoutAction<R>
): [ReducerStateWithoutAction<R>, DispatchWithoutAction];

여기에 매칭이 되더라고요. 

vscode로 보면 

 function useReducer<R extends Reducer<anyany>>(
        reducer: R,
        initialState: ReducerState<R>,
        initializer?: undefined
    ): [ReducerState<R>, Dispatch<ReducerAction<R>>];

여기에 매칭이 되기는 하는데 어쨌든 IDE에나 컴파일해봤을 때나 같은 에러가 뜹니다. 

이건 어떤 식으로 해결할 수 있을까요? 

감사합니다. 

reacttypescript

Câu trả lời 2

0

mhr님의 프로필 이미지
mhr
Người đặt câu hỏi

제로초님 안녕하세요. 빠른 답변 감사합니다. 

function useReducer<R extends ReducerWithoutAction<any>, I>(
reducer: R,
initializerArg: I,
initializer: (arg: I) => ReducerStateWithoutAction<R>
): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
/**
* An alternative to `useState`.
*
* `useReducer` is usually preferable to `useState` when you have complex state logic that involves
* multiple sub-values. It also lets you optimize performance for components that trigger deep
* updates because you can pass `dispatch` down instead of callbacks.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
*/
// overload where dispatch could accept 0 arguments.
function useReducer<R extends ReducerWithoutAction<any>>(
reducer: R,
initializerArg: ReducerStateWithoutAction<R>,
initializer?: undefined
): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
/**
* An alternative to `useState`.
*
* `useReducer` is usually preferable to `useState` when you have complex state logic that involves
* multiple sub-values. It also lets you optimize performance for components that trigger deep
* updates because you can pass `dispatch` down instead of callbacks.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
*/
// overload where "I" may be a subset of ReducerState<R>; used to provide autocompletion.
// If "I" matches ReducerState<R> exactly then the last overload will allow initializer to be omitted.
// the last overload effectively behaves as if the identity function (x => x) is the initializer.
function useReducer<R extends Reducer<any, any>, I>(
reducer: R,
initializerArg: I & ReducerState<R>,
initializer: (arg: I & ReducerState<R>) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
/**
* An alternative to `useState`.
*
* `useReducer` is usually preferable to `useState` when you have complex state logic that involves
* multiple sub-values. It also lets you optimize performance for components that trigger deep
* updates because you can pass `dispatch` down instead of callbacks.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
*/
// overload for free "I"; all goes as long as initializer converts it into "ReducerState<R>".
function useReducer<R extends Reducer<any, any>, I>(
reducer: R,
initializerArg: I,
initializer: (arg: I) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
/**
* An alternative to `useState`.
*
* `useReducer` is usually preferable to `useState` when you have complex state logic that involves
* multiple sub-values. It also lets you optimize performance for components that trigger deep
* updates because you can pass `dispatch` down instead of callbacks.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
*/

// I'm not sure if I keep this 2-ary or if I make it (2,3)-ary; it's currently (2,3)-ary.
// The Flow types do have an overload for 3-ary invocation with undefined initializer.

// NOTE: without the ReducerState indirection, TypeScript would reduce S to be the most common
// supertype between the reducer's return type and the initialState (or the initializer's return type),
// which would prevent autocompletion from ever working.

// TODO: double-check if this weird overload logic is necessary. It is possible it's either a bug
// in older versions, or a regression in newer versions of the typescript completion service.
function useReducer<R extends Reducer<any, any>>(
reducer: R,
initialState: ReducerState<R>,
initializer?: undefined
): [ReducerState<R>, Dispatch<ReducerAction<R>>];

위의 파일이 제가 현재 사용하고 있는 버전의 useReducer가 정의되어 있는 react의 index.d.ts 파일인데요. 

리턴값들을 보시면 첫 번째와 두 번째 정의의 리턴값은 DispatchWithoutAction을 리턴하지만, 나머지 3, 4, 5번째의 리턴값의 dispatch 함수는 Dispatch<ReducerAction<R>> 입니다. 이 경우는 action을 받을 수 있는 거 아닌가 생각했는데요. 

리액트 노드버드 강좌 들을 때 제 나름대로 타이핑할 때 dispatch 함수에 제네릭을 줬던 기억이 나서 확인해보니 useDispatch는 react-redux의 index.d.ts 파일에 

export function useDispatch<TDispatch = Dispatch<any>>(): TDispatch;
export function useDispatch<A extends Action = AnyAction>(): Dispatch<A>;

이렇게 정의되어 있더라고요. useDispatch의 Dispatch<any>나 위의 useReducer의 Dispatch<ReducerAction<R>>이나 마찬가지인 것 같아서 그렇게 사용할 수 있지 않을까 생각했습니다. 그래서 위의 useReducer 타입 정의 중에서 마지막 것에 맞춰보려고 useReducer의 generic을 이래저래 다르게 줘봐도 잘 안 되더라고요. 오버로딩 가능한 것들 중에서 엉뚱한 것을 가져와서 에러가 나는 경우에는 타입을 맞춰보라고 강의 중에 말씀하셨던 것 같은데, 이 경우는 어떻게 해결할 수 있을지 궁금하네요. 감사합니다. 

zerocho님의 프로필 이미지
zerocho
Người chia sẻ kiến thức

저 코드에 따르면 dispatch는 Dispatch<ReducerAction<R>>인데 이미 <R>이 정해져있습니다. 저희가 useReducer할 때 제네릭으로 넣었습니다(Reducer<ReducerState, ReducerActions>). ReducerAction<R>은 그 R(Reducer)에서 ReducerActions를 꺼내는 것이고요. 따라서 dispatch에는 이미 ReducerActions가 적용되어 있습니다. 그래서 dispatch는 아무런 제네릭을 받지 못합니다.

mhr님의 프로필 이미지
mhr
Người đặt câu hỏi

그런 것이었군요. 이해했습니다. 감사합니다!

0

zerocho님의 프로필 이미지
zerocho
Người chia sẻ kiến thức

제네릭은 마음대로 쓸 수 없습니다. 이미 useReducer을 호출할 때 dispatch의 매개변수가 ReducerActions로 결정되었습니다. 

dispatch({ type: SET_WINNER, winner: turn });

dispatch는 제네릭을 받지 않습니다.

Hình ảnh hồ sơ của rmh03130151
rmh03130151

câu hỏi đã được viết

Đặt câu hỏi