TypeScript: 'Element implicitly has an 'any' type' 동적 객체 키 에러 해결법 (keyof & Record & Index Signature)

TypeScript 객체 키 any 임플리시트 오류 해결 및 인덱스 시그니처 가이드
3초 요약: 이렇게 고치세요
  • 원인: string 타입 변수로 객체 키에 접근하면 그 변수가 실제로 어떤 키를 담을지 컴파일 시점에 알 수 없어서 발생
  • 해결: 키가 고정돼 있으면 as keyof typeof 객체 단언 / 키가 계속 늘어나면 인덱스 시그니처([key: string]: Type) 또는 Record<string, Type>
  • 주의: API 응답 JSON을 그대로 순회하며 key로 값을 꺼내 쓸 때 특히 자주 마주치는 에러
const safeKey = key as keyof typeof USER_ROLES;
return USER_ROLES[safeKey];
↓ 왜 이런 에러가 나는지 원리 보기

자바스크립트에서는 문제없이 동작하던 동적 객체 키 접근 코드가 TypeScript로 옮기면 'Element implicitly has an any type' 경고를 띄우는 경우가 많습니다. 이 경고가 발생하는 이유와, 상황별로 쓸 수 있는 해결 방법 세 가지를 정리합니다.


타입스크립트는 왜 동적 키 접근을 에러로 잡는가

타입스크립트의 가장 큰 목적은 컴파일 단계에서 타입 안정성을 확보하는 것입니다. 예를 들어, 객체에 어떤 키가 존재할지 예측 불가능한 상황에서 임의의 변수를 문자열 키로 집어넣어 접근하려고 하면, 타입스크립트는 해당 결과값이 존재하지 않을 수 있어 위험(undefined 가능성)하다고 판단합니다. 그래서 기본값인 any 타입으로 암묵적으로 처리하며 경고를 내뿜게 되는 것입니다. API 응답으로 받은 JSON 객체를 그대로 순회하면서 key로 값을 꺼내 쓸 때 특히 자주 마주치는 에러입니다.

TypeScript의 엄격한 키 매핑 구조

동적 변수 키 (컴파일 차단) const key: string으로 선언된 변수는 어떤 문자열이든 담길 수 있으므로 객체의 키 목록과 일치하지 않아 차단됨.
타입 가드 완료 (통과) 객체의 명확한 키 목록(keyof)으로 제한하거나, 타입 매핑(Index Signature)을 통해 타입을 명시하면 통과함.

'implicitly has an any type' 해결을 위한 3가지 방법

방법 A: keyof와 typeof 연산자를 활용한 타입 단언 (가장 추천)

객체의 키 값이 사전에 정의되어 있는 고정된 구조일 때 가장 우아하게 통과할 수 있는 정석 해결 방법입니다. 변수의 타입을 해당 객체의 키들 중 하나(keyof)로 강제 매칭시킵니다.

const USER_ROLES = {
  admin: "관리자",
  editor: "편집자",
  viewer: "일반사용자"
};

// 에러 발생: key 변수의 string 타입이 USER_ROLES의 키들과 호환되지 않음
function getRoleName(key: string) {
  return USER_ROLES[key];
}

// 해결:
function getRoleNameSafe(key: string) {
  // key 변수를 USER_ROLES 객체의 키 목록 중 하나로 단언(Assertion)
  const safeKey = key as keyof typeof USER_ROLES;
  return USER_ROLES[safeKey];
}

방법 B: 인덱스 시그니처(Index Signature) 정의하기

객체의 키 이름이 동적으로 계속 늘어나거나 사전 정의하기 힘들 때 유용한 설계 방식입니다. 객체 타입 자체에 문자열 키가 매핑될 수 있음을 미리 가이드합니다.

// 인덱스 시그니처 타입 선언 [key: string]: string
interface DynamicUser {
  name: string;
  [key: string]: string; // 임의의 문자열 키 접근 허용
}

const user: DynamicUser = {
  name: "김철수",
  department: "R&D",
  city: "Seoul"
};

function getValue(key: string) {
  return user[key]; // 오류 없이 동작
}

방법 C: Record 유틸리티 제네릭 타입 활용하기

타입스크립트가 내장하여 제공하는 Record<Key, Value> 구조를 활용하면 복잡한 인덱스 시그니처 인터페이스 없이도 동적 객체를 빠르게 타이핑할 수 있습니다.

// Key는 string 타입, Value는 number 타입으로 이루어진 동적 맵
const productPrices: Record<string, number> = {
  macbook: 2500000,
  iphone: 1300000
};

function getPrice(product: string): number {
  return productPrices[product] ?? 0; // 타입 체커 통과
}

자바스크립트로 짜여있던 관리자 대시보드를 타입스크립트로 옮기던 중, 설정 화면의 다국어 라벨을 labels[currentLang]처럼 동적 키로 꺼내 쓰는 코드가 유독 많았습니다. 처음엔 코드마다 as any를 붙여서 일단 빌드부터 통과시켰는데, 나중에 다시 보니 어디가 진짜 위험한 접근이고 어디가 안전한 접근인지 구분이 안 됐습니다. 결국 언어 목록처럼 키가 고정된 곳은 keyof typeof 단언으로, 키가 계속 늘어나는 설정값은 Record로 나눠서 정리했습니다.


요약: 동적 객체 접근 문제 해결 맵

키 목록이 고정된 객체를 임의의 문자열 키로 접근할 때는 as keyof typeof 객체 형태로 타입을 단언해 주는 것이 가장 정석적입니다. 키 목록이 수시로 바뀌는 구조라면 [key: string]: Type 인덱스 시그니처를 정의해 두면 됩니다. 단순한 Key-Value 맵이라면 Record<string, Type> 유틸리티 타입을 쓰는 편이 인덱스 시그니처를 매번 선언하는 것보다 간결합니다.

공식 참고 레퍼런스

타입 지정을 활용한 객체 제어에 대한 내용은 공식 문서에서 확인할 수 있습니다.

호마다의 웹개발 팁

키 목록이 고정돼 있다면 keyof 단언이 가장 안전하고, 키가 계속 늘어나는 구조라면 인덱스 시그니처나 Record가 더 적합합니다. 타입스크립트의 객체 타입 기초가 헷갈린다면 [호마다의 IT 개발 입문 블로그] 도 참고해보세요.

댓글

이 블로그의 인기 게시물

TypeScript: Unknown vs Any 타입 차이점과 안전한 타입 가드(Type Guard)

Next.js 하이드레이션 오류 원인과 해결법 완벽 정리

Next.js App Router에서 getServerSideProps 대체 및 데이터 페칭법