Item 16 number 인덱스 시그니처보다는 Array, 튜플, ArrayLike를 사용하기

Object (객체)

자바스크립트에서 객체란 키/값 쌍의 모음이며, 키는 보통 문자열이다. 그리고 값은 어떤 것이든 가능하다.

Python, Java와 달리 '해시 가능 객체'라는 표현이 자바스크립트에는 없다. 만약 복잡한 객체를 사용하려고 하면, toString 메서드가 호출되어 객체가 문자열로 변환된다.

> x = {}
{}
> x[[1, 2, 3]] = 2
2
> x
{ : 1 }

Array (배열)

배열은 객체이다.

> typeof []
'object'

그러니 숫자 인덱스를 쓰는 것이 당연하지만, 인덱스는 문자열로 변환되어 사용되므로, 문자열 키로도 배열의 요소에 접근할 수 있다.

> x = [1, 2, 3]
[1, 2, 3]
> x[0]
1
> x['1']
2
> Object.keys(x)
[ '0', '1', '2' ]

TS는 이러한 혼란을 바로잡고자 숫자 키를 허용하고, 문자열과 다른 것으로 인식한다.

interface Array<T> {
  // ...
  [n: number]: T
}

런타임에는 ECMAScript 표준이 서술하는 것처럼 문자열 키로 인식하므로, 이 코드는 가상이라고 할 수 있다. 하지만 타입 체크 시점에 오류를 잡을 수 있으므로 유용하다.

const xs = [1, 2, 3]
const x0 = xs[0] // OK
const x1 = xs['1']
           // ~~~ 인덱스 식이 'number' 형식이 아니므로 요소에 암시적으로 'any' 형식이 있습니다.

function get<T>(array: T[], k: string): T {
  return array[k]
            // ~ 인덱스 식이 'number' 형식이 아니므로 요소에 암시적으로 'any' 형식이 있습니다.
}

위 코드는 실제로 동작하지 않는다. 그리고 TS 타입 시스템의 다른 것들과 마찬가지로 타입 정보는 런타임에 제거된다. Item 3 코드 생성과 타입이 관계없음을 이해하기

한편 Object.keys 같은 구문은 여전히 문자열로 반환된다.

const keys = Object.keys(xs) // 타입이 string[]
for (const key in xs) {
  key                        // 타입이 string
  const x = xs[key]          // 타입이 number
}

stringnumber에 할당될 수 없으므로 예제의 마지막 줄이 동작하는 것이 동작하는 것이 이상하게 보일 것이다. 배열을 순회하는 코드 스타일에 대한 실용적인 허용이라고 생각하는 것이 좋다. JS에서는 흔하지만 위처럼 순회하는 것은 좋지 않다. 인덱스가 필요한 게 아니라면 for-of를 사용하는 것이 더 좋다.

for (const x of xs) {
  x // 타입이 number
}

만약 인덱스의 타입이 중요하다면, number 타입을 제공해 줄 Array.prototype.forEach를 사용하면 된다.

xs.forEach((x, i) => {
  i // 타입이 number
  x // 타입이 number
})

루프 중간에 멈춰야 한다면 C 스타일인 for(;;) 루프를 사용하자.

for (let i = 0; i < xs.length; i++) {
  const x = xs[i]
  if (x < 0) break
}

타입이 불확실하다면, for-in 루프는 for-of 또는 C 스타일 for 루프에 비해 몇 배나 느리다.

어떤 길이를 가지는 배열과 비슷한 형태의 튜플을 사용하고 싶다면 TS의 ArrayLike 타입을 사용한다.

function checkedAccess<T>(xs: ArrayLike<T>, i: number): T {
  if (i < xs.length) {
    return xs[i]
  }
  throw new Error(`배열의 끝을 지나서 ${i}를 접근하려고 했습니다.`)
}

ArrayLike를 사용하더라도 키는 여전히 문자열이다.

Summary

  • 배열은 객체이므로 키는 숫자가 아니라 문자열이다. 인덱스 시그니처로 사용된 number 타입은 버그를 잡기 위한 순수 타입스크립트 코드이다.

  • 인덱스 시그니처에 number를 사용하기보다 Array나 튜플, 또는 ArrayLike 타입을 사용하는 것이 좋다.

Last updated