[Effective TypeScript] - any 다루기(2)

이예슬·2022년 12월 19일
0

Effective TypeScript

목록 보기
14/15

Item41. any의 진화를 이해하기

타입스크립트는 일반적으로 변수를 선언할 때 타입 또한 결정된다. 그 후에 정제될 수 있지만 확장은 할 수 없다. 그러나 any 타입과 관련해서는 예외인 경우가 존재한다.(any는 진짜 예외가 너무 많다…)

function range(start: number, limit: number){
	const out = [] // 타입이 any[]
	for(let i = start; i < limit; i++){
		out.push(i); // out의 타입이 any[]
	} 
	return out; // 타입이 number[]
}

out의 타입이 처음에는 any의 배열이었는데 return 할 때의 타입은 number의 배열인 것을 확인할 수 있다. out의 타입은 any[]로 선언되었지만 number 타입의 값을 넣는 순간부터 타입은 number[]로 진화한다.

이는 위와 같은 코드에서 뿐만 아니라 조건문에 따른 분기에 따라서도 변화될 수 있으며 블록 안에서 변수를 할당하는 경우에도 나타난다.

이러한 any 타입의 진화는 noImplicitAny가 설정된 상태에서 변수의 타입이 암시적 any 인 경우에만 일어난다. 하지만 명시적으로 any를 선언하면 타입이 그대로 유지된다.

즉 any 타입의 진화는 암시적 any 타입에 어떤 값을 할당할 때만 발생한다. 하지만 타입을 안전하게 지키기 위해서는 암시적 any를 진화시키는 방식보다 명시적 타입 구문을 사용하는 것이 더 좋은 설계이다.

Item42. 모르는 값에는 any 대신 unknown을 사용하기

unknown 타입을 이해하기 위해서는 할당 가능성의 관점에서 any를 생각해 볼 필요가 있다.

  • 어떠한 타입이든 any 타입에 할당 가능하다
  • any 타입은 어떠한 타입으로도 할당 가능하다.

any는 위 두 가지 특징을 가진다. 이는 타입 시스템을 집합의 관점에서 바라볼 때 “한 집합은 다른 모든 집합의 부분 집합이면서 동시에 상위집합이 될 수 없다” 라는 점에서 상충된다. 그리고 이러한 점이 any가 강력하면서도 문제를 일으키는 원인이 된다. 집합 기반인 타입 체커는 any를 사용할 경우 무용지물이 된다.

unknown은 any 대신 쓸 수 있는 타입 시스템에 부합하는 타입이다.

  • 어떠한 타입이든 unknown 타입에 할당 가능하다.
  • unknown 타입은 어떠한 타입으로도 할당 가능하지 않다!

unknown 타입인 채로 값을 사용하거나 연산을 하면 오류가 발생한다. 때문에 unknown 사용시 적절한 타입으로 변환하도록 강제할 수 있다.

어떠한 값이 있지만 그 타입을 모르는 경우에 unknown을 사용한다. 제너릭보다는 unknown을 반환하고 사용자가 직접 단언문을 사용하거나 원하는 대로 타입을 좁히도록 강제하는 것이 좋다.

any의 경우 분리되는 순간 그 영향력이 전염병처럼 퍼지게 된다. 그러나 unknown의 경우 분리되는 즉시 오류를 발생하게 되므로 더 안전하다.

{}, object, unknown의 차이

  • {} 타입은 null 과 undefined를 제외한 모든 값을 포함한다.
  • object 타입은 모든 비기본형 타입으로 이루어진다.

null과 undefined가 불가능하다고 판단되는 경우 unknown 대신 {}를 사용하면 된다.

Item43. 몽키 패치보다는 안전한 타입을 사용하기

몽키 패치란?

프로그램이 런타임 되는 동안 사용되는 모듈이나 클래스를 변경하는 행동을 일컫는다.

원래는 ‘guerrilla patch’ 라고 불리던게 고릴라 패치라고 잘못 불리게 되면서 고릴라보다 왜소한 원숭이로 대체해서 불리기 시작했다.

자바스크립트 기준으로 몽키 패치란 런타임 중에 property object를 직접적으로 수정하는 일련의 작업들을 말한다.

자바스크립트의 가장 큰 특징 중 하나는 객체와 클래스에 임의의 속성을 추가할 수 있을만큼 유연하다는 것이다. window 또는 DOM 노드에 데이터를 추가할 경우 그 데이터는 기본적으로 전역 변수가 된다. 전역 변수를 사용하게 되면 관련 없는 코드 사이에도 의존성을 만들어 함수를 호출할 때 부작용을 일으킬 수 있다. 타입스크립트를 사용하게 될 경우 타입 체커는 Document와 HTMLElement의 내장 속성은 알고 있지만 임의로 추가한 속성에 대해서는 알지 못하므로 추가적인 문제가 발생한다.

해당 오류를 해결하는 가장 간단한 방법은 any 단언문을 사용하는 것이지만 이는 타입 안전성을 상실시키고 언어 서비스를 사용할 수 없게 한다. 가장 좋은 해결책은 document 또는 DOM으로부터 데이터를 분리하는 것이다.

  • interface 보강을 사용한다.
  • 더 구체적인 타입 단언문을 사용하기

Item44. 타입 커버리지를 추적하여 타입 안전성 유지하기

noImplicitAny를 설정하고 모든 암시적 any 대신 명시적 타입 구문을 추가해도 any 타입이 여전히 존재하는 경우가 있다.

  • 명시적 any 타입
  • 서드파티 타입 선언

아래의 npm 패키지는 전체 프로젝트에서 any를 추적해준다.

// 전체 프로젝트 중 any가 몇 개인지 확인할 수 있다. 
npx type-coverage 
// any 타입이 있는 모든 곳을 출력해준다. 
npx type-coverage-detail

<이펙티브 타입스크립트> Dan Vanderkam, 프로그래밍 인사이트 (2021)

profile
꾸준히 열심히!

0개의 댓글