TypeScript

Type Assertion, 타입 단언의 올바른 사용법 고민하기

3jun 2023. 9. 1. 11:02

타입 단언은 타입에 확신이 있을 때 사용하는 타입 지정 방식이다. 나 같은 경우에는 `Unsafe member access ~~` 에러가 발생했을 때 주로 타입 단언을 통해 해결했었다. 

 

타입 단언 사용방법

타입 단언은 기본적으로 as 키워드를 사용하여 정의한다. 

const name = '김씨' as string

타입 단언은 타입 에러를 해결하기 위한 좋은 수단이지만 다음 몇가지 사항을 고려해서 사용해야 한다.

타입 단언 사용 시 고려해야할 것들

1. 타입 안정성 : Type Assertion, 즉 타입 단언을 사용하게 되면 Typescript 유형 검사를 우회하게 된다. 따라서 잘못된 타입으로 단언이 되어 있는 경우 런타임 오류가 발생하거나 실제 값과 일치하지 않는 타입 단언으로 인해 사이드 이펙트가 발생할 수 있다.

2. 유지 관리 : 타입 단언을 과도하게 사용하면 코드 유지보수에 악영햘을 줄 수 있다. 코드의 구조나 형태가 변경되는 경우 그에 영향 받는 모든 타입 단언도 변경된 내용에 맞게 함께 업데이트를 해줘야 하기 때문이다.

3. 타입 추론 : Typescript 는 명시적으로 타입을 지정하지 않아도 해당 Type에 대해 추론을 할 수 있다. 가능한 경우에는 유형 추론에 의존함으로써 불필요한 타입 단언을 줄이고 Type의 안정성을 유지하는게 좋다.

4. 복잡한 Type Casting : 정확하게 Type casting이 필요한 중첩 구조의 경우 타입 단언을 사용하면 위험할 수 있다. 복잡한 환경에서 연관된 데이터, 이벤트 구조와 타입 단언이 정확하게 일치하는지 항상 확인해야하기 때문이다.

 

복잡한 Type Casting을 명확하게 확인할 수 있는 사례를 바로 어제 경험하여 공유하고자 한다.

어제 겪은 에러가 딱 복잡한 Type Casting과 연관된 에러였기에 예시가 궁금하신 분은 아래 부분을 참고하면 되겠다.

 

Error | 중첩된 객체(객체 안의 객체) 형태의 상태 onChange 함수 구현하기

중첩된 객체, 즉 객체 안의 객체 형태의 state 가 존재할 때, 이 state 를 업데이트 하기 위한 onChange 함수를 구현하는 방법을 공유하고자 한다. 에러 발생 상황 제목 그대로 중첩된 객체 형태의 상태

kk3june.tistory.com

그럼 위 포스팅은 중첩된 데이터 구조의 타입 에러를 타입 단언으로 해결한 사례인데, 중첩된 구조이다 보니 타입 단언 역시 복잡해졌다. 

복잡한 Type Casting 코드 개선하기 ( aka. 타입 단언 사용하지 않기)

const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target;
    const [key, innerKey] = name.split('-');  // name = 'address-city' , 'address-province'

   const updatedState = {...state};
   (updatedState[key as keyof OverLappedState] as Address)[innerKey as keyof Address] = value
 };

위 코드로 타입에러를 해결하기는 했지만 타입 단언이 과도하게 사용되어 데이터 구조, 2가지 타입 중 한 군데에서라도 수정이 발생하면 다시 코드를 작성해야하는 유지보수에 굉장히 불편한 코드임을 한 눈에 볼 수 있다. 

개선된 코드

const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = event.target;
    const [key, innerKey] = name.split('-');  // name = 'address-city' , 'address-province'
	
    if(key !== 'address') return;
    if(!(innerKey in state[key])) return;
    
    const updatedState = { ...state, [key]: {...state[key], [innerKey]: value} }
};

이렇게 처리할 데이터 구조를 제외하고는 해당 함수에서 사용하지 않도록 작성하면 타입 단언 없이도 원하는 기능을 구현할 수 있다.

이렇게 구현하기 훨씬 가독성도 좋고 유지보수도 편하게 코드를 작성할 수 있었다. 

 

💡 any와 마찬가지로 타입단언 역시 불필요한 경우가 아니면 지양하기로 하자.
     개발 잘하는 사람들의 코드를 보고 공부해야하는 이유!!