Logo

객체를 순회하다가 발생하는 타입스크립트 오류 해결하기

자바스크립트만 하시다가 처음으로 타입스크립트를 하시게 되면 매우 흔하게 겪는 문제가 있습니다. 바로 자바스크립트에서 하던데로 객체를 순회하는 코드를 짜다보면 자꾸 예상치 못한 타입 오류에 부딪치게 되는 것인데요.

이번 포스팅에서는 객체를 순회하다가 발생할 수 있는 타입 오류를 해결하는 방법에 대해서 알아보겠습니다.

Object.keys()

간단한 실습을 위해서 TypeScript Playground에서 객체를 순회하는 코드를 같이 작성해볼까요? Object.keys() 함수로 객체의 키 배열을 얻고, 이 배열을 forEach() 함수로 순회해보겠습니다.

const obj = { a: 1, b: 2, c: 3 };
Object.keys(obj).forEach((key) => console.log(obj[key])); // 오류 발생 🚨

이 코드를 에서 실행해보면 다음과 같은 오류가 발생하는 것을 볼 수 있습니다. (코드)

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ a: number; b: number; c: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ a: number; b: number; c: number; }'.

오류 메시지를 해석해보면 { a: number; b: number; c: number; } 형태의 객체를 문자열(string) 자료형의 키로 접근할 수 없다는 건데요. 원인은 Object.keys() 함수의 반환형이 string[]이기 때문에, forEach() 함수에서 key의 자료형이 string으로 추론된 것입니다.

그러면 어떻게 이러한 타입 오류없이 문자열을 키로 해서 객체를 순회할 수 있을까요? 여기서 문제는 타입스크립트 컴파일러가 주어진 객체의 현재 모습만 보고 키가 반드시 a 또는 b 또는 c 중에 하나여야 한다고 생각하는 것입니다.

사실 우리는 이 객체에 언제든지 obj[d] = 4와 같이 다른 키로 값을 추가할 수 있잖아요? 따라서 obj의 자료형을 Record<string, number>로 명시하여 타입스크립트 컴파일러에게 객체의 키가 아무 문자열이든 상관없다고 알려주면 됩니다.

const obj: Record<string, number> = { a: 1, b: 2, c: 3 };
Object.keys(obj).forEach((key) => console.log(obj[key]));

위 코드는 아무런 오류가 발생하지 않고, 콘솔에 1, 2, 3이 차례로 잘 출력될 거에요 👍 (코드)

for-in

자바스크립트 개발자들 중에는 Object.keys() 대신에 for-in 구문을 사용해서 객체를 순회하기도 하시죠?

const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(obj[key]); // 오류 발생 🚨
}

안타깝게도 객체가 할당되어 있는 변수의 자료형을 명시적으로 지정해주지 않으면 아까와 동일한 타입 오류가 발생하게 됩니다. (코드)

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ a: number; b: number; c: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ a: number; b: number; c: number; }'.

원인은 타입스크립트에서 for-in 구문을 사용하면 key는 항상 문자열로 취급이 되기 때문인데요. obj의 자료형이 { a: number; b: number; c: number; }이기 때문에 타입 스크립트 컴파일러는 문자열인 keyobj를 접근하는 것은 위험하다고 판단하는 것이지요.

이 타입 오류는 Object.keys()와 마찬가지로 obj의 자료형을 Record<string, number>라고 명시해주면 사라지게 됩니다. (코드)

const obj: Record<string, number> = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(obj[key]);
}

또 다른 편법으로는 자바스크립트의 typeof 키워드와 타입스크립트의 keyof 키워드를 사용해서 객체의 키 자료형을 추출 후에 타입스크립트의 as 키워드를 사용하여 key의 자료형을 "a" | "b" | "c"로 강제해주는 것이 있는데요. (코드)

const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(obj[key as keyof typeof obj]);
}

저는 개인적으로는 이 방법은 잘 추천하지 않습니다. 이런 식으로 타입을 강제하는 것은 애초에 타입스크립트를 사용하려는 취지와 어긋나는 것 같습니다.

Object.values()

이렇게 객체를 순회할 때 마다 해당 객체의 자료형을 직접 명시해줘야 것이 상당히 번거롭게 느껴질 수도 있는데요. 사실 Object.values()Object.entries()를 사용하면 굳이 키를 통해서 객체의 값에 접근할 필요가 없어지기 때문에 좀 더 편하게 타입 오류의 걱정으로 해방될 수 있습니다 😁 (코드)

const obj = { a: 1, b: 2, c: 3 };
Object.values(obj).forEach((value) => console.log(value));
1
2
3

마치면서

자바스크립트에서 아무 생각없이 하던 짜던 코드를 타입스크립트로 짤 때는 많은 신경을 써야 하는 경우가 많은데요. 특히 이렇게 별 것도 아닌 코드를 짜다가 막하기 되면 더욱 의욕을 상실하기 쉬운 것 같습니다. 본 포스팅이 타입스크립트를 시작하자 마자 좌절하시는 분들에게 작은 도움이 되었으면 좋겠네요 🙏