빠른 설정
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true
}
}| 상황 | 먼저 볼 기준 |
|---|---|
| 배열 인덱스 접근이 많다 | 값 없음 처리가 필요한지 확인 |
Record<string, T>를 자주 쓴다 | 모든 키가 실제로 존재하는지 확인 |
| 외부 입력을 key-value로 받는다 | 접근 결과를 먼저 좁힌 뒤 사용 |
| 오류가 너무 많이 늘어난다 | 경계 모듈부터 단계적으로 적용 |
무엇이 바뀌나
noUncheckedIndexedAccess를 켜면 인덱스로 접근한 값에 undefined 가능성이 붙습니다. 타입 선언상 "문자열 키면 모두 값이 있다"처럼 보이던 구조도 실제 런타임에서는 해당 키가 없을 수 있기 때문입니다.
const names = ["Ada", "Grace"];
const first = names[0];
const maybeThird = names[2];옵션이 꺼져 있으면 names[2]도 단순한 string처럼 다뤄지기 쉽습니다. 옵션을 켜면 인덱스 접근 결과를 바로 사용할 수 없고, 존재 확인이나 기본값 처리가 필요합니다.
const maybeName = names[2];
if (maybeName) {
console.log(maybeName.toUpperCase());
}Record<string, string>이나 index signature도 같은 영향을 받습니다. 타입상 모든 문자열 키가 가능하더라도, 실제 객체에 그 키가 들어 있다는 뜻은 아닙니다.
const labels: Record<string, string> = {
save: "Save",
};
const label = labels["delete"];
if (label !== undefined) {
console.log(label.toUpperCase());
}적용하면 좋은 곳
API 응답, 환경 변수, URL query, JSON 파싱 결과처럼 실제 키 존재가 보장되지 않는 곳에서 효과가 큽니다. 특히 Record<string, T>를 임시 map처럼 쓰는 코드에서는 없는 키 접근이 런타임 오류로 이어지기 쉽습니다.
function getHeader(headers: Record<string, string>, key: string) {
const value = headers[key.toLowerCase()];
return value ?? "";
}배열에서도 index 접근을 자주 한다면 오류를 조기에 드러냅니다. items[0]이 항상 있다고 가정하는 코드가 많다면 find, at, 길이 검사, 기본값 중 어떤 계약이 맞는지 명확히 해야 합니다.
function firstUpper(items: string[]) {
const first = items[0];
return first ? first.toUpperCase() : "";
}오류를 줄이는 방식
가장 단순한 방식은 존재 확인입니다. 값이 없을 때도 정상 흐름이 있다면 ??로 기본값을 두고, 값이 반드시 있어야 한다면 guard 함수나 명시적 예외를 둡니다.
const value = map[key];
if (value === undefined) {
throw new Error(`Missing key: ${key}`);
}
return value;키 집합이 고정되어 있다면 Record<string, T> 대신 literal union 기반 Record<Key, T>를 쓰는 편이 더 정확합니다. 임의 문자열 키와 닫힌 키 집합은 다른 모델입니다.
type Locale = "ko" | "en";
const messages: Record<Locale, string> = {
ko: "저장",
en: "Save",
};주의할 점
noUncheckedIndexedAccess를 켠 뒤 오류를 없애려고 !를 반복해서 붙이면 옵션을 켠 의미가 줄어듭니다.
값이 없는 경우가 실제로 가능한지 확인하고, 기본값이나 guard로 계약을 드러내십시오.
대규모 프로젝트에 한 번에 적용하면 오류가 크게 늘 수 있습니다. 새 코드, 외부 입력 경계, map 유틸리티처럼 실패 비용이 큰 영역부터 적용하고, 공통 helper를 만든 뒤 범위를 넓히는 흐름이 안정적입니다.
function required<T>(value: T | undefined, message: string): T {
if (value === undefined) {
throw new Error(message);
}
return value;
}참고 링크
1 sources