[Typescript] 고급 타입 - any vs unknown

mainsain·2024년 2월 12일
0

Typescript

목록 보기
4/8
post-thumbnail

타입스크립트의 타입 계층 구조이다.

any 타입

자바스크립트에 존재하는 모든 값을 오류 없이 받을 수 있다.
즉, 타입을 명시하지 않은 것과 동일하다.

let state: any;

state = { value: 0 }; // 객체를 할당해도
state = 100; // 숫자를 할당해도
state = "hello world"; // 문자열을 할당해도
state.foo.bar = () => console.log("this is any type");
// 심지어 중첩 구조로 들어가 함수를 할당해도 문제없다

당연히 이 any 타입은, 지양해야 할 패턴으로 알려져있다. 그러나, 아래 3가지의 경우 사용을 고려해보자.

개발 단계에서 임시로 값을 지정해야 할 때

개발 과정에서 추후 값이 변경될 가능성이 있거나, 타입이 확정되지 않은 경우

  • 👍🏻 타입 명시하는 시간을 절약할 수 있다.
  • 👎🏻 남발하면 타입 안정성을 저해한다.

어떤 값을 받아올지, 넘겨줄지 정할 수 없을 때

API 요청 및 응답 처리, 콜백함수 전달, 정제되지 않은 외부 라이브러리 등 특정하기 힘든 경우

type FeedbackModalParams = {
    show: boolean;
    content: string;
    cancelButtonText?: string;
    confirmButtonText?: string;
    beforeOnClose?: () => void;
    action?: any;
  };
  • 위 예제에선, action이란 속성이 any로 선언되어 있다.
  • 다양한 범주의 액션에 따라 인자의 개수나 타입을 명시하기 힘들 수 있다.

값을 예측할 수 없을 때 암묵적으로 사용

외부 라이브러리나 웹 API의 요청에 따라 다양한 값을 반환하는 API가 존재할 수 있다.

async function load() {
  const response = await fetch("https://api.com");
  const data = await response.json();
	// response.json()의 리턴 타입은 Promise<any>로 정의되어 있다
  return data;
}
  • 대표적으로 Fetch API의 일부 메서드는 반환 타입이 any로 매핑되어있다.

unknown 타입

unknown : 무엇이 할당될지 아직 모르는 상태

let unknownValue: unknown;

unknownValue = 100; // any 타입과 유사하게 숫자이든
unknownValue = "hello world"; // 문자열이든
unknownValue = () => console.log("this is any type"); // 함수이든상관없이할당이가능하지만

let someValue1: any = unknownValue; // (O) any 타입으로 선언된 변수를 제외한 다른 변수는 모두 할당이 불가
let someValue2: number = unknownValue; // (X)
let someValue3: string = unknownValue; // (X)
  • any : 어떤 타입이든 any 타입에 할당 가능하며 어떤 타입으로도 할당 가능
  • unknown : 어떤 타입이든 unknown 타입에 할당 가능하며 any를 제외한 다른 타입으로 할당 불가능

위 예제에서 흥미로운건, 함수를 unknown타입 변수에 할당할때는 컴파일러가 경고를 주지 않지만, 실행하면 아래와 같은 에러가 발생한다.

// 할당하는 시점에서는 에러가 발생하지 않음
const unknownFunction: unknown = () => console.log("this is unknown type");

// 하지만 실행 시에는 에러가 발생; Error: Object is of type 'unknown'.ts (2571)
unknownFunction();

함수 뿐만 아니라 객체의 속성 접근, 클래스 생성자 호출을 통한 인스턴스 생성 등 객체 내부에 접근하는 모든 시도에서 에러가 발생한다.

unknown 타입은 어떤 타입이 할당되었는지 알 수 없음을 나타내는 역할이기에, 어떤 값이든 올 수 있음과 동시에 개발자에게 엄격한 타입 검사를 강제하는 의도를 담고있다.

any를 사용하면 어떤 값이든 허용된다. 그러나 특정타입으로 수정하지 않고 누락하면 예상치못한 버그가 발생할 수 있다.

unknown은 이러한 상황을 보완하기 위해 등장한 타입이다. any와 유사하지만 타입 검사를 강제하고, 식별된 후에 사용할 수 있기에 any보다 안전하다.

결론

  1. any는 아무때나 쓸 수 있지만, typescript를 쓰는 의미가 없게 되어버릴 수 있다.
  2. unknown을 어쩔 수 없이 써야할 상황이 있을 수도 있으나, 이를 사용하는 쪽에서의 방어처리를 해서 안전하게 사용가능하다.

위와 같은 이유로, 궁극적으로 우리는 any를 사용하지 않기로 정했고, 피치 못하게 사용해야 할 상황이나, 꼭 필요한 상황에는 unknown을 사용하기로 정하였습니다. 그렇게 해서, 사용하는 쪽에서 한 번의 처리를 더 강제하고, 안전하게 사용을 유도하는 것이죠.

https://techblog.woowahan.com/9804/#toc-2

🎙️ 우아한 형제들 인터뷰 (우아한 타입스크립트 with 리액트)

❓ any를 사용하나요? 사용한다면 어떤 상황에서 사용할까요?

냥이팀 : any는 웬만하면 쓰지 말자고 얘기하고 있어요. 특히 런타임에 오류를 방지하고 싶다면 any를 쓰지 말아야죠. 하지만 특정 타입을 어떻게 좁혀서 사용할지 모를 때 어쩔 수 없이 any를 사용한 적이 있어요. 응답객체의 구조를 정확히 몰랐죠. 하지만 지금 생각해보니 any를 쓸 필요가 없는 것 같아서 리팩터링하며 전부 고치고 있어요.
타입스크립트 베이스로 데이터 플로우가 다 들어있다면 any가 필요한 상황이 거의 없겠죠. 그러나 Axios 라이브러리의 응답 객체 기본값이 any인걸 보면 필요한 상황이 있을 것 같긴해요
그러나 깨진 유리창 이론처럼, any를 한번 사용하면 남발하게 될까 경계하고 있습니다.

감자팀 : 웬만하면 any를 지양하려 하고 있어요. 하지만 아예 안쓰기엔 어려운 것 같아요. 표 컴포넌트같이 어떤 값을 받을 지 모르는 상황에서 unknown을 사용하면, 가공할때 타입 캐스팅을 전부 해야해서 이럴 때 any를 사용하고 있습니다.

❓ unknown은 어떨 때 사용할 수 있을까요?

배달이팀 : 강제 타입 캐스팅을 통해 타입을 전환할 때 사용합니다. const env = process.env as unknown as ProcessEnv 같은 식으로요.

냥이팀 : any보다는 좀 더 많이 사용하는 것 같아요. any는 무엇이든 괜찮다. unknown은 뭔지 모르지만 하나씩 테스트하며 알아내보자 라는 의미라고 생각하는데, 후자의 논리전개가 필요하다면 unknown을 사용합니다. 또 unknown으로 선언되면 any와 달리 에러가 발생해서 더 엄격해서 unknown이 마음에 듭니다.

왕팀 : 예상할 수 없는 데이터라면 unknown을 사용합니다. 타입스크립트 4.4부터 try - catch에러의 타입이 any에서 unknown으로 변경되어 에러 핸들링할 때도 unknown을 사용합니다. 또 as unknown as Type같이 강제 타입 캐스팅을 하기도 하는데 사실 이것도 any랑 다를게 없어서 지양해야합니다.

profile
새로운 자극을 주세요.

0개의 댓글