필드명(fieldname)에 따라 필드 값(field value) 타입이 다를 때의 타입스크립트(typescript) 타입 적용 방법

Maria Kim·2023년 4월 7일
0
post-thumbnail

typescipt를 이용해 redux 프로젝트를 진행하며 field 명 따른 field 타입을 value로 가져와야 하는 상황을 마주했다.

상황

// redux state의 타입
export interface StockList {
  mainInfo: StockMainInfo;
}

export interface StockMainInfo {
  stockName: string;
  currentPrice: number;
  stockId: number;
};
// 해당 컴포넌트 파일

const onInputChange = (
  e: React.ChangeEvent<HTMLInputElement>,
  fieldName: keyof Omit<StockMainInfo, 'stockId'>,
) => {
  dispatch(
    updateStock({
      stockIdx: stockIdx,
      fieldName: fieldName,
      value: e.target.value,
    }),
  );
};

...

<Input
  onChange={(e) => onInputChange(e, 'stockName')}
  value={stockInfo.mainInfo.stockName}
/>
<Input
  onChange={(e) => onInputChange(e, 'currentPrice')}
  value={stockInfo.mainInfo.currentPrice}
/>
  • 1개의 컴포넌트 내에 여러 개의 input이 있음
  • 똑같은 action 적용 -> param 갯수 같음 -> onInputChange 1개 작성 가능
  • StockMainInfo 상태 중 stockName와 currentPrice 만 Update 가능
  • stockName 타입 ≠ currentPrice 타입
    - stockName 의 타입은 string, currentPrice의 타입은 number

문제

  • 위 상황에서 아래 reducer 액션 updateStock을 타입 가드 없이 아래와 같이 사용하기 위해서는 어떻게 해야할까?
export const stockListSlice = createSlice({
  name: 'stockList',
  initialState,
  reducers: {
    updateStock:(state, action) => {
          const { stockIdx, fieldName, value } = action.payload;
          state.stocks[stockIdx].mainInfo[fieldName] = value;
    };
  }
});

찾아보자

정말 100개도 넘는 redux, reduxToolkit와 typescript 관련 사이트, Stackoverflow, 유튜브까지 찾아봤지만 진짜 신기하게 분명 다들 이 비슷한 상황을 마주했을 텐데 나오지 닮은 코드가 나오지 않았다... (도대체 왜 모든 typescript와 reduxToolkit의 예시는 count, todo 만들기 인가... )

그중 진짜 비슷한 Stackoverflow 질문을 찾았지만... 답이 없었다...
(논외로, 이 Stackoverflow 글을 보고 절망했다. 진짜 너무 절망적... 그래서 내가 꼭 답을 찾아 답을 작성하리라 다짐했고 진짜 오늘 처음으로 Stackoverflow 답변 글을 작성했다! 오예!!!)
Stackoverflow - Proper way to add types to Redux-toolkit reducer

방법을 드디어 찾다!

그래도 정말 다양한 자료를 학습하고 ChatGPT에게 다양한 답을 요구하며 (사용할 수 있는 코드가 이상하게 계속 끝까지 나오지 않았다...)

두둥!!! 그래도 조금 깔끔한 하나의 방법을 찾았다!


type UpdateStockPayload<T extends keyof StockMainInfo> = {
  stockIdx: number;
  fieldName: T;
  value: StockMainInfo[T];
};

export const stockListSlice = createSlice({
  name: 'stockList',
  initialState,
  reducers: {
    updateStock: <T extends keyof Omit<StockMainInfo, 'stockId'>>(
          state: StockListState,
          action: PayloadAction<UpdateStockPayload<T>>,
      ) => {
          const { stockIdx, fieldName, value } = action.payload;
          state.stocks[stockIdx].mainInfo[fieldName] = value;
    };
  }
});

UpdateStockPayload에서 모든 타입을 작성하고 함수에서 제너릭을 선언하지 않는 방법을 계속 찾아봤지만 위 방법이 최선인 듯했다...(방법을 아시는 분 제발 알려주세용 ㅠㅜ)

많은 삽질과 업 다운이 있었지만 타입스크립트와 조금 더 친해진 시간이었다!

제발 친해지자 타입스크립트!

더 많은 타입스크립트 코드들이 public으로 공유되길 바라며
오늘도 Happy Coding 되세용!!

profile
Frontend Developer, who has business in mind.

0개의 댓글