"객체 타입의 키를 하나씩 순회하며 같은 규칙으로 새 모양을 만든다."
타입 수준의 반복문
{ [K in keyof T]: T[K] }는 T의 모든 키를 차례로 가져와 같은 키와 값 타입으로 새 객체 타입을 만든다. 그대로 쓰면 원본과 같은 모양이지만, 프로퍼티 표시에 규칙을 더하면 유용한 변환이 돼. 예를 들어 { [K in keyof T]?: T[K] }가 바로 Partial<T>의 핵심 원리야.
여기서 K는 반복 중인 현재 키이고 T[K]는 그 키에 대응하는 값 타입이야. 값 타입을 전부 boolean으로 바꾸거나, 조건부 타입으로 특정 프로퍼티만 감싸는 식으로 변형할 수 있어. 중요한 건 키와 값의 관계를 잃지 않은 채 전체 객체에 일관된 규칙을 적용한다는 점이지.
선택성과 읽기 전용을 더하고 빼기
?와 readonly 수정자는 매핑 과정에서 조절할 수 있어. +?는 선택성을 더하고 -?는 없애며, +readonly와 -readonly도 같은 방식으로 작동해. 더하기 기호는 기본값이라 흔히 생략하지만, 빼기 기호는 기존 속성을 제거할 때 꼭 필요해.
type MutableRequired<T> = {
-readonly [K in keyof T]-?: T[K];
};
이 타입은 모든 프로퍼티에서 readonly와 선택성을 함께 제거해. 다만 타입에서 읽기 전용 표시를 지웠다고 이미 얼어 있는 런타임 객체가 바뀌는 것은 아니야. 정적 계약과 실제 객체 동작을 구분해야 해.
as로 키 이름까지 바꾸기
{ [K in keyof T as NewKey]: ... } 형태를 쓰면 값을 바꾸는 데서 그치지 않고 키를 다시 만들 수 있어. 문자열 키 앞에 get을 붙여 getter 모음을 만들거나, 조건에 맞지 않는 키를 never로 보내 제외하는 패턴이 대표적이야.
키 재매핑은 강력하지만 지나치면 타입 오류가 암호문처럼 변해. 도메인에서 반복되는 실제 규칙을 표현할 때 쓰고, 한 번만 등장하는 작은 객체를 멋져 보이게 만들려고 쓰지는 마. 읽는 사람이 변환 전후를 머릿속에서 복원할 수 있어야 좋은 타입이야.
매핑 타입의 as 키 재매핑은 TypeScript 4.1에서 추가됐어. 템플릿 리터럴 타입과 함께 쓰면 각 키에 접두사를 붙이거나 조건에 맞지 않는 키를 never로 보내 제외할 수 있어.