nameHeightMap
은 임의의 유저 목록을 받아 유저의 이름을 키로, 유저의 신장을 값으로 갖는 매핑이다. 이 때 이 객체의 키들은 임의의 유저 이름이므로 코드를 작성하는 시점에는 모든 가능한 키를 나열하는 것이 불가능하다. 예를 들어 위 타입을 아래와 같이 정의한다고 해 보자.users
값에 { name: '뉴페이스', height: 777 }
등의 유저가 추가되는 경우를 제대로 처리하지 못 할 것이다. 또한 실제로는 users
와 같은 정보를 실행 시간에 서버로부터 얻어오는 등의 경우가 많은데, 이런 경우 역시 커버할 수 없다. 이럴 때 필요한 것이 바로 색인 가능 타입(indexable type)이다. []
)를 이용해 객체의 색인 시그니쳐(index signature)를 적어줘야 한다.NameHeightMap
인터페이스는 색인 가능 타입을 사용해 아래와 같이 적을 수 있다.NameHeightMap
타입의 값을string
타입 값 userName
으로 색인한 값 ([userName: string]
→ 인덱스 시그니쳐)nameHeightMap[userName]
은 number
또는 undefined
타입의 값이다. (: number | undefined
)number
가 아닌 number | undefined
타입을 가지는 것에 유의하라. nameHeightMap
이 모든 문자열을 키로 갖고 있다는 보장이 없으므로 nameHeightMap['없는 유저']
따위의 값은 undefined
일 수 있기 때문이다.number
타입의 값으로 사용하고 싶다면 먼저 undefined
인지 여부를 체크해줘야 한다.B
는 A
의 서브타입이어야 한다.B
가 A
의 서브타입이다”는 말의 의미는 “B
타입의 모든 값은 A
타입에도 속한다” 정도로 이해할 수 있다. 예를 들어, 모든 정수를 나타내는 타입 Int
와 모든 숫자를 나타내는 타입 Num
이 존재한다고 하자. 모든 정수는 숫자이므로 (즉 Int
타입의 모든 값을 Num
타입의 값으로도 사용할 수 있으므로) Int
는 Num
의 서브타입이다.toString()
메소드를 호출해 문자열로 변형된 값을 색인으로 사용한다. 예를 들어 1.toString() === '1'
이므로 obj[1]
이라고 적은 코드는 실제로는 obj['1']
와 동일하다.ErrorProne
타입과 같이 숫자로 색인 된 값의 타입(boolean
)이 문자열로 색인 된 타입(number
)의 서브타입이 아닌 경우가 허용된다고 가정해보자.3
이라는 색인은 숫자 타입이므로 타입 시스템은 errorProne[3]
의 타입이 boolean
일 것이라 추측할 것이다. 하지만 위에서 언급한 색인의 동작 방식에 의해 실제로 해당 값은 errorProne['3']
과 같고, 이는 문자열 색인으로 접근한 number
타입의 값이다. 타입 시스템이 알고 있는 정보(boolean
)와 실제 상황(number
) 이 달라지는 것이다.error TS2413: Numeric index type 'boolean' is not assignable to string index type 'number'.
와 같은 에러를 발생시킨다. 숫자 색인으로 접근한 타입 boolean
을 문자열 색인으로 접근한 타입 number
에 할당할 수 없다는 의미다.user.name === user['name']
이므로) 결국 문자열 색인 접근의 특수한 케이스이기 때문이다. 아래와 같은 선언은 타입 에러를 발생시킨다.readonly
의 동작과 마찬가지로 readonly
로 선언된 색인의 값은 재할당이 불가능하다.Array
인터페이스를 꼽을 수 있다. 만약 색인 가능 타입이 없이 T
타입의 원소를 갖는 Array
인터페이스를 작성한다면 대략 아래와 같은 식으로 모든 색인에 대한 타입을 일일이 정의해야 할 것이다.