2026-09-10: 설명과 코드 예시를 보완했습니다. 아래 예시는 글의 설계 의도를 전달하기 위한 것이며, 원 프로젝트에 반영된 변경 내역과는 구분합니다.
객체를 순회할 때 Object.entries()를 자주 쓴다. 그런데 키를 확인한 뒤 그 키에 맞는 값의 메서드를 쓰려면 타입이 아쉬울 때가 있다. 값이 유니온으로 추론되더라도, 어느 키에 어느 값이 대응하는지까지 그대로 유지되지는 않을 수 있기 때문이다.
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 대상이 아니다. 이 예시는 이런 객체나 배열, 임의의 외부 응답을 모두 다루는 범용 함수로 사용하면 안 된다.
외부 데이터라면 스키마를 확인하거나 필요한 키를 명시해 순회하는 방법을 고려할 수 있다. 타입에 적힌 키와 실제로 열거될 키가 같은지 먼저 확인해야 한다.
그 조건을 아는 내부 객체에서는 이 짧은 함수가 제법 편하다. 매번 같은 as를 길게 적는 대신, 필요한 단언을 한곳에 두고 순회하는 코드에는 순회할 내용만 남길 수 있으니까.
Assisted by AI
© uiwwsw · 본인 소유의 글·이미지에 대한 무단 복제·재게시를 허용하지 않습니다. 개인 열람·원문 링크 공유·검색 색인과 제한적인 미리보기는 허용합니다. 법령상 이용 예외, 기존 이용 허락, 코드·제삼자 자료의 기존 라이선스는 유지합니다. 콘텐츠 이용 안내