blog

TypeScript에서 빈 배열의 0번째 요소에 접근하면 에러가 날까?

Front-end / TypeScript#TypeScript#noUncheckedIndexedAccess#배열#tsconfig

결론: 컴파일 에러도, 런타임 에러도 나지 않는다. 런타임에서는 undefined가 나오는데, 기본 설정의 TypeScript는 이것을 알려주지 않는다.

런타임 (JavaScript)

존재하지 않는 인덱스에 접근하면 에러가 아니라 undefined를 반환한다.

const arr: number[] = [];
console.log(arr[0]); // undefined

컴파일 타임 (TypeScript 기본 설정)

const arr: number[] = [];
const first = arr[0]; // 타입: number (undefined가 빠져 있음!)

first.toFixed(2); // 컴파일 통과 → 런타임 TypeError

기본 설정에서 number[]의 인덱스 접근 결과는 number로 추론된다. 실제 값은 undefined인데 타입은 number라서, 이후 코드에서 런타임 에러가 날 수 있다.

noUncheckedIndexedAccess

tsconfig.json에서 이 옵션을 켜면 인덱스 접근 결과에 undefined가 포함된다.

{
  "compilerOptions": {
    "noUncheckedIndexedAccess": true
  }
}
const arr: number[] = [];
const first = arr[0]; // 타입: number | undefined

first.toFixed(2); // ❌ 'first' is possibly 'undefined'
first?.toFixed(2); // ✅

Record<string, T> 같은 인덱스 시그니처 접근에도 똑같이 적용된다. strict에는 포함되지 않으므로 따로 켜야 한다. 안전성이 크게 올라가는 대신 arr[i]마다 확인이 필요해지므로, 반복문 안에서는 for...of나 forEach처럼 인덱스를 쓰지 않는 순회를 쓰면 편하다.

안전하게 접근하는 방법

// 1. 기본값 지정
const first = arr[0] ?? 0;

// 2. at(): 옵션과 관계없이 항상 T | undefined로 추론됨
const last = arr.at(-1);

// 3. 길이 확인 후 접근
if (arr.length > 0) {
  const first = arr[0];
}

// 4. 구조 분해
const [head] = arr; // 기본 설정에서는 number, 옵션을 켜면 number | undefined

길이를 확인해도 noUncheckedIndexedAccess를 켠 상태에서는 TypeScript가 arr[0]을 여전히 number | undefined로 본다. 길이 검사로는 타입이 좁혀지지 않기 때문이다. 이때는 const first = arr[0]; if (first !== undefined) { ... }처럼 값 자체를 검사한다.