아이템 33. String 타입보다 더 구체적인 타입 사용하기

아이템 33. String 타입보다 더 구체적인 타입 사용하기

아이템 33. string 타입보다 더 구체적인 타입 사용하기 #

string 타입의 범위는 매우 넓다.(“a”, “b”, “casdsad”, …)

다음은 string 타입이 남발된 타입의 예시이다.

interface Album {
    artist: string;
    title: string;
    releaseDate: string;    // 예를 들어, yyyy-mm-dd
    recordingType: string;  // 예를 들어, "live" 또는 "studio"
}

string 은 다음과 같은 단점이 있다.

예를 들어,

  • recordingType 에 “Live” 값이 들어가도, 타입 체커는 완벽하게 체크할 수 없다.
  • releaseDate 에 “asdsad” 값이 들어가도, 타입체커는 완벽하게 체크할 수 없다.
  • 혹은 매개변수의 순서가 바뀌어도 (releaseDate 위치에 recordingType 값을 넣는다던가…)



다음과 같이 개선해볼 수 있다. #

type RecordingType = 'studio' | 'live';

interface Album {
    artist: string;
    title: string;
    releaseDate: Date;
    recordingType: RecordingType;
}

다음과 같은 이점이 있다.

  1. (타입을 명시적으로 정의함으로써) 다른 곳으로 값이 전달되어도, 타입 정보가 유지된다.
    1. 즉, 타입 정보를 계속 갖고간다.
  2. 타입을 명시적으로 정의하고, 해당 타입의 의미를 설명하는 주석을 붙여 넣을 수 있다.
    1. IDE 상에서 주석 및 타입 정보를 쉽게 확인 가능하다.
  3. keyof 연산자로 더욱 세밀하게 타입 체크가 가능하다.

아래는 추가적인 개선 예시이다.

// 개선 X
function pluck<T>(records: T[], key: string): any[] {
    return records.map(r => r[key]);
                        // ~~~~~~~~ '{}' 형식에 인덱스 시그니처가 없으므로 요소에 암시적으로 'any' 형식이 있습니다.
}
// 개선
function pluck<T>(records: T[], key: keyOf T) {
    return records.map(r => r[key]);
}
// 개선의 개선 
function pluck<T, K extends keyof T>(records: T[], key: K): T[K][] {
    return records.map(r => r[key]);
}

위(개선의 개선)와 같은 경우, 매개변수 타입이 정밀해진 덕분에 (IDE 단에서)자동완성 기능도 제공될 수 있다.



string 은 any와 비슷한 문제를 가지고 있다. #

무효한 값을 허용하고, 타입 간의 관계도 감춰버린다.

타입 체커를 방해하고, 실제 버그를 찾지 못하게 한다.

string의 부분 집합을 정의하는 것은, js에 타입 안정성을 크게 높인다.



요약 & 정리 #

  • string 을 남발하여 선언된 코드를 피하자.
  • 변수의 범위를 보다 정확하게 표현하자.
    • string 타입보다는 리터럴 타입의 유니온을 사용할 수 있다.
  • 객체의 속성 이름을 함수 매개변수로 받을 때는, string 보다 keyof T 를 사용하자.