본문으로 바로가기
윤창원uiwwsw · 작은 우주

개발과 기술

Object.entries의 키 타입을 보완하기 — typedEntries의 조건과 한계

2026-09-10: 설명과 코드 예시를 보완했습니다. 아래 예시는 글의 설계 의도를 전달하기 위한 것이며, 원 프로젝트에 반영된 변경 내역과는 구분합니다.

객체를 순회할 때 Object.entries()를 자주 사용한다. TypeScript에서는 값이 유니온으로 추론되더라도 키와 값의 구체적인 대응 관계가 그대로 유지되지는 않을 수 있다.

const user = { name: 'Alice', age: 30 };
const entries = Object.entries(user);
// 키는 string으로 넓어진다.
// 값의 타입은 적용되는 라이브러리 선언과 인자 타입에 따라 달라진다.

반환값이 언제나 [string, any][]이라고 단정하는 것은 정확하지 않다. 내가 보완하고 싶은 것은 특히 name에는 문자열, age에는 숫자가 대응한다는 관계다.

닫힌 객체에 사용하는 보조 함수

다음 예시는 문자열 키만 가진 일반 객체를 대상으로 한다. 해당 값에 선언된 키 외의 enumerable 속성이 없다는 조건을 호출자가 알고 있을 때 사용하는 타입 단언이다.

type StringEntry<T extends object> = {
  [K in Extract<keyof T, string>]-?: [K, T[K]]
}[Extract<keyof T, string>];

export function typedEntries<T extends object>(obj: T): StringEntry<T>[] {
  return Object.entries(obj) as StringEntry<T>[];
}

const user = { name: 'Alice', age: 30 };
for (const [key, value] of typedEntries(user)) {
  if (key === 'age') {
    value.toFixed();
  }
}

각 키의 튜플을 유니온으로 만들었기 때문에 키를 좁혔을 때 연결된 값도 좁힐 수 있다. [keyof T, T[keyof T]] 하나로 표현하면 키와 값 각각의 유니온은 남지만 이 대응 관계는 약해진다.

단언이 보장하지 않는 것

TypeScript의 객체 타입은 런타임 속성 목록을 봉인하지 않는다. TypeScript 객체 타입

const full = { name: 'Alice', age: 30 };
const named: { name: string } = full;
Object.entries(named); // 런타임에는 age도 포함된다.

이 값에 typedEntries(named)를 적용하면 타입에는 없는 age가 실제로 나타난다. 보조 함수에 as를 모았다고 런타임 검증이 생긴 것은 아니다.

숫자 키는 문자열로 반환되고, Symbol 키는 Object.entries 대상이 아니다. 이 예시는 이런 객체나 배열, 임의의 외부 응답을 모두 다루는 범용 함수로 사용하면 안 된다.

외부 데이터라면 스키마를 확인하거나 필요한 키를 명시해 순회하는 방법을 고려할 수 있다. 객체의 조건을 확실히 아는 내부 코드에서는 보조 함수로 반복을 줄일 수 있다.

이 유틸의 가치는 타입을 완벽하게 안전하게 만드는 데 있지 않다. 어떤 가정을 하는지 한곳에 드러내고, 그 가정이 맞는 범위에서 키와 값의 관계를 더 편하게 사용하는 데 있다.

Assisted by AI

윤창원이 벨로그에 남긴 글을 이 작은 우주에도 모았습니다. 사진은 누르면 원본 크기로 볼 수 있습니다. 원문의 전체 서식 보기 ↗

모든 글 둘러보기 →