typescript 에서 객체의 키값에 접근할 때 주의사항

조민호·2023년 6월 6일
0
post-custom-banner

  • 특정 api를 호출하게 되면 아래와 같이 받아와진다
    {
        buy: [ // 구매 목록
          { tradeDate: '2023-03-22T18:55:07', saleSn: '641acd4ea430e79103bdb95e', spid: 279228251, grade: 5, value: 13300000000 },
          { tradeDate: '2023-03-22T18:55:07', saleSn: '641acd4ea430e79103bdb95e', spid: 279228251, grade: 5, value: 13300000000 },
    			...	
        ],
        sell: [ // 판매 목록
          { tradeDate: '2023-03-22T18:55:07', saleSn: '641acd4ea430e79103bdb95e', spid: 279228251, grade: 5, value: 13300000000 },
          { tradeDate: '2023-03-22T18:55:07', saleSn: '641acd4ea430e79103bdb95e', spid: 279228251, grade: 5, value: 13300000000 },
    			...
        ],
     }
  • 객체배열 안에 있는 요소의 타입을 TradeLogInfo로 지정했다
    // { "tradeDate": "2023-05-31T01:07:25",
    // "saleSn": "64761f3cad134d6873034f84",
    // "spid": 265204024,
    // "grade": 1,
    // "value": 499000000 }
    interface TradeLogInfo {
      tradeDate: string;
      saleSn: string;
      spid: number;
      grade: number;
      value: number;
    }
  • 이걸 리액트에서 api 반환값을 tradeInfo라는 상태로 관리하고 있다
    const [tradeInfo, setTradeInfo] = useState<{ buy: TradeLogInfo[]; sell: TradeLogInfo[] } | null>(null);

여기서 , 특정 로직에 의해 sell , buy 라는 부분을 분리해서 따로 보여주고자 할 때 (초깃값은 buy)

tradeType이라는 상태를 새로 만들고, 여기에 buy 나 sell 이라는 값을 넣어서

대괄호 표기법을 이용하여 tradeInfo의 키에 접근하는 것이다

const [tradeInfo, setTradeInfo] = useState<{ buy: TradeLogInfo[]; sell: TradeLogInfo[] } | null>(null);
...

const [tradeType, setTradeType] = useState('buy');
// type inference 때문에 굳이 string으로 지정할 필요 x
// const [tradeType, setTradeType] = useState<string>('buy');
...

return (
	<>
		{tradeInfo[tradeType].map((i) => {...}}
	</>
)


근데 에러가 발생한다

'string' 형식의 식을 '{ buy: TradeLogInfo[]; sell: TradeLogInfo[]; }' 인덱스 형식에 사용할 수 없으므로 요소에 암시적으로 'any' 형식이 있습니다.
'{ buy: TradeLogInfo[]; sell: TradeLogInfo[]; }' 형식에서 'string' 형식의 매개 변수가 포함된 인덱스 시그니처를 찾을 수 없습니다.ts(7053)

이 문제는 TS는 객체의 키를 컴파일 시점에 검증하기 때문에,

tradeType 변수의 값이 'buy'나 'sell'임을 알 수 없다

그러므로 TS는 tradeType이 잠재적으로 tradeInfo 객체의 유효하지 않은 키일 수 있다고 판단하므로 에러를 발생시키는 것이다

string 타입으로 지정해줘도 마찬가지이다

string 타입은 'buy'와 'sell' 뿐만 아니라 JavaScript에서 가능한 모든 문자열을 포함하므로

TypeScript는 tradeType이 tradeInfo의 키로 사용될 때 안전하다고 판단할 수 없다

간단한 예시로

tradeType이 "axvnjzklds"와 같은 값으로 설정될 경우

tradeInfo["axvnjzklds"]는 존재하지 않으므로, TypeScript는 이러한 가능성을 염두에 두고 에러를 발생시키는 것이다



useState의 초깃값으로 ‘buy’ 를 지정 했는데도 buy나 sell임을 알 수 없다는건가?

결론부터 말하자면

초깃값은 buy로 보장을 하더라도, 추후 동적으로 어느 값으로 바뀔지 모르기 때문에 에러를 발생시키는 것이다

TS 는 정적으로 타입을 검사하기 때문에 코드를 실행하지 않고,

컴파일 시점 코드를 분석하여 타입 안정성을 검증한다

그 말은 useState의 초기값으로 'buy'를 넣었지만, tradeType의 값은 실행 시간에 변경될 수 있다는 것이며

예를 들어, 나중에 setTradeType 함수를 호출하여 tradeType의 값을

'anvlkqwelas' 같은 이상한 값으로 변경할 수 있게 된다

따라서 TypeScript는 tradeType이 초기에 'buy'였더라도 이후에 어떤 값이 될지 확신할 수 없기 때문에 사전에 에러를 발생시키는 것이다



해결

TS 가 더 정확한 추론을 할 수 있게 하려면, useState에서 가능한 상태를 모두 지정해야 한다

따라서, tradeType이 무조건 'buy' 또는 'sell' 중 하나임을 TypeScript에게 알려줘야 한다

즉 , 아래 둘 중 하나의 방법으로 해결이 가능하다

  • useState에서 명확하게 정의 하는 방법
    const [tradeType, setTradeType] = useState<'buy' | 'sell'>('buy');
  • tradeType이 'buy' 또는 'sell'일 때만 tradeInfo에 접근하게 하는 방법
    if (tradeType === 'buy' || tradeType === 'sell') {
      console.log(tradeInfo![tradeType]);
    }
  • type assertion 사용하기
    // 어차피 string 타입
    const [tradeType, setTradeType] = useState('buy');
    
    // string 타입이지만 무조건 'buy' 아니면 'sell'을 보장
    type buyOrSell = 'buy' | 'sell'
    console.log(tradeInfo![tradeType as buyOrSell]);
    
post-custom-banner

0개의 댓글