Frontend/Typescript

아이템 16. number 인덱스 시그니처보다는 Array, 튜플, ArrayLike 사용하기

rachel_13 2023. 4. 23. 21:47

https://develub.kr/shop_view/?idx=26

자바스크립트에서 객체의 키는 보통 '문자열'이다.  배열은 객체이기 때문에,

Object.keys를 이용해서 배열의 키를 나열할 수도 있다. 배열의 인덱스를 나열해보면 문자열로 변환되는 것을 쉽게 알 수 있다. 

타입스크립트에서는 이러한 혼란을 방지하기 위해 숫자 'Key(키)'를 허용하고, 문자열 키와 다른 것으로 인식한다.

 

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

런타임 시점에서는 키를 모두 문자열로 인식할 것이므로,  이 코드는 타입 체크 시점에 오류를 잡는 용도로 사용할 수 있겠다.

** 여기서 사용된 number 타입은 버그를 잡기 위한 순수 타입스크립트 코드임을 명심하자! **

 

const x = [1, 2, 3];
const keys = Object.keys(x);
console.log(keys); //문자열로 반환됨

for (const key in x){
  key; //type이 string
  const xk = x[key]; //type이 number
}

for...in.. 루프에서 인덱스에 string이 들어갔는데 허용이 되는 부분을 보면 이상하다고 느낄 수 있다.

이는 배열을 순회하는 실질적인 허용이다. 자바스크립트에서 흔한 예제이지만, for..in을 사용하여 배열을 순회하는 것은 좋은 방법은 아니다. 대부분의 브라우저와 자바스크립트 엔진에서 타입이 불확실할 경우 for-in 루프는 for-of 또는 C스타일 for 루프에 비해 느리기 때문이다.

 

(참고_1) for_of 루프 ~ 인텍스 타입이 그다지 중요하지 않은 경우

for(const key of x){
  key; //type이 number
}

(참고_2) forEach ~ 인덱스 타입이 중요한 경우

x.forEach((xe, i) => {
  xe; //type이 number 
  i; //type이 number;
})

 

(참고_3) C스타일 for문 루프 ~ 중간에 멈춰야 하는 경우

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

 

어쨌든 인덱스 시그니처가 number로 표현되어 있을 경우, 입력값도 number이어야 한다고 생각하지만, 실제 런타임에 사용되는 키의 타입은 string이기 때문에 앞서 '실질적인 허용'이라고 표현한것과 같다.

 

만약 number 타입을 인덱스 항목으로 지정하고 싶을 경우에는 인덱스 시그니처 보다는 Array / 튜플 / ArrayLike 속성을 활용하는 것이 좋다.

ArrayLike: 배열과 비슷한 형태의 튜플이면서 길이 속성을 가진 타입 (단, 키는 여전히 문자열이다.)

function CheckedAccess<T>(x: ArrayLike<T>, i:number):T {
  if(i < x.length){
    return x[i]
  }
  throw new Error("배열 종료")
}