아이템 05. Any 타입 지양하기

아이템 05. Any 타입 지양하기

아이템 5. any 타입 지양하기 #

타입스크립트 타입 시스템은 ‘점진적(gradual)’, ‘선택적(optional)’ 이다.

  • 점진적 : 코드에 타입을 조금씩 추가할 수 있다.
  • 선택적 : 언제든지 타입 체킹을 해제할 수 있다.

이 기능의 핵심은 ‘any’ 타입이다.


하지만 (일부 특별한 경우를 제외하고는) any 를 사용하지 않는 것을 권장한다.



any 타입에는 ‘타입 안정성’이 없다. #

예시 : any 사용 X

let age: number;
age = '12'; // 에러 발생 (error TS2322: Type 'string' is not assignable to type 'number'.)
» tsc example.ts
example.ts:2:1 - error TS2322: Type 'string' is not assignable to type 'number'.

2 age = '12';
  ~~~

Found 1 error in example1.ts:2

» cat example.js
var age;
age = '12';

예시 : any 사용 O

let age: number;
age = '12' as any;
» tsc example.ts
» cat example.ts
var age;
age = '12';



any는 함수 시그니처를 무시한다. #

즉, 트랜스파일의 결과물(output)은 동일하지만,
타입 체크의 도움을 받을 것인지, 받지 않을 것인지를 말하고 있는 것이다.

예시 : any 사용 X

function calculateAge(birthDate: Date): number {
    // ...
    return 1;
}

let birthDate = '2000-01-01';
calculateAge(birthDate);
» tsc example.ts
example.ts:7:14 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'Date'.

7 calculateAge(birthDate);
               ~~~~~~~~~

Found 1 error in example.ts:7

» cat example.js
function calculateAge(birthDate) {
    // ...
    return 1;
}
var birthDate = '2000-01-01';
calculateAge(birthDate);

예시 : any 사용 O

function calculateAge(birthDate: Date): number {
    // ...
    return 1;
}

let birthDate: any = '2000-01-01';
calculateAge(birthDate);
» tsc example.ts
» cat example.js
function calculateAge(birthDate) {
    // ...
    return 1;
}
var birthDate = '2000-01-01';
calculateAge(birthDate);



any 타입에는 언어 서비스가 적용되지 않는다. #

여기서 말하는 언어 서비스란?
자동 완성, 변수명 변경(= 전체 변경해주는 기능, intellij 의 refactor) 등의 서비스를 의미한다.

자동완성, 변수명 변경 등의 서비스를 제공받지 못한다.

예시 생략

타입스크립트의 모토는 ‘확장 가능한 자바스크립트’이다. ‘확장’의 중요한 부분 중 하나가 ‘언어 서비스’이다. (언어 서비스를 통해 개발자의 생산성이 향상되기 때문이다.)



any 타입은 코드 리팩토링 때 버그를 감춘다. #

예시 : any 사용 X

아래 코드는 ComponentProp 의 타입은 number 임에도 타입 체커가 별도의 에러를 발생시키지 않는다.
즉, 잠재적 버그를 감춘다.

let selectedId: number = 0;

interface ComponentProps {
    onSelectItem: (item: number) => void;
}

function handleSelectItem(item: any) { // ComponentProps 구현체
    selectedId = item.id;
}

function renderSelector(props: ComponentProps) { 
    /* ... */
}

renderSelector({onSelectItem : handleSelectItem})
» tsc example.ts
» cat example.js
var selectedId = 0;
function handleSelectItem(item) {
    selectedId = item.id;
}
function renderSelector(props) {
    /* ... */
}
renderSelector({ onSelectItem: handleSelectItem });

예시 : any 사용 O

let selectedId: number = 0;

interface ComponentProps {
    onSelectItem: (item: any) => void;
}

function handleSelectItem(item: any) { // ComponentProps 구현체
    selectedId = item.id;
}

function renderSelector(props: ComponentProps) { 
    /* ... */
}

renderSelector({onSelectItem : handleSelectItem})
» tsc example.ts
» cat example.js
var selectedId = 0;
function handleSelectItem(item) {
    selectedId = item.id;
}
function renderSelector(props) {
    /* ... */
}
renderSelector({ onSelectItem: handleSelectItem });



any는 타입 설계를 감춘다. #

인터페이스, 클래스, 등 다양한 속성을 정의할 때 any 타입을 사용하면 코드의 설계를 감춘다.

예를 들어 아래 Person 클래스 코드를 살펴보자.

class Person {
    personId: any;
    constructor(personId: any) {
        this.personId = personId;
    }
}

let p1 = new Person('P1011');
let p2 = new Person(1);

personId 의 타입이 any 라면,

  • number 가 들어갈 지
  • string 이 들어갈 지

직접 코드를 보고 판단해야한다.



any는 타입시스템의 신뢰도를 떨어뜨린다. #

+ 위의 예시들과 같이 타입 체커가 본 역할을 제대로 수행하지 않으면, 타입스크립트를 사용하는 이유가 없어지는 것과 같을 것이다.

(이상적인 목표) 타입 체커가 실수를 잡아주고, 개발자는 타입스크립트(타입 시스템), 코드에 대한 신뢰도가 생성된다.

하지만 any 타입을 사용할수록 타입 체커가 제대로 된 역할을 제대로 수행하지 못할 것이고, 신뢰도를 떨어뜨리게 할 것이다.



요약 #

1. any 타입의 사용을 최대한 지양한다.

2. any 타입을 사용하면 타입 체커와 타입스크립트 언어 서비스를 무력화시킨다.

  • any 타입은 ‘문제점’을 감춘다.
  • any 타입은 개발 경험을 나쁘게한다.
  • any 타입은 신뢰도를 떨어뜨린다.