숏컷 코드
type ReadonlyUser<T> = {
readonly [K in keyof T]: T[K];
};
type PrefixKeys<T> = {
[K in keyof T as `app_${string & K}`]: T[K];
};매핑 구조
mapped type에서 먼저 보는 기본형은 아래입니다.
- 전체 프로퍼티 순회:
[K in keyof T]: ... - optional/readonly modifier 조정:
+?,-?,readonly,-readonly - 키 이름 변경:
[K in keyof T as NewKey]: ... - 일부 키 제거:
as never
1) 기존 객체 타입 전체를 한 규칙으로 바꾸면 mapped type
[K in keyof T]는 기존 키 집합을 순회하며 새 객체 타입을 만드는 패턴입니다.
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};즉 객체 타입 전체를 기계적으로 변형하는 기본 도구라고 보면 됩니다.
2) modifier도 함께 조정할 수 있다
mapped type은 값 타입만 바꾸는 것이 아니라 readonly, ? 같은 프로퍼티 modifier도 추가/제거할 수 있습니다.
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
type Concrete<T> = {
[K in keyof T]-?: T[K];
};이 점 때문에 유틸리티 타입 대부분이 결국 mapped type 위에 서 있습니다.
3) 키 이름 자체를 바꾸면 key remapping
as를 이용하면 키를 필터링하거나 접두사를 붙이는 식으로 이름을 바꿀 수 있습니다.
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};일부 키를 제거하고 싶다면 remapping 결과를 never로 보내는 패턴도 자주 보입니다.
type WithoutKind<T> = {
[K in keyof T as Exclude<K, "kind">]: T[K];
};이 패턴은 강력하지만 코드 생성 느낌이 강해서 과용하면 읽기 난도가 빠르게 올라갑니다.
4) 표준 유틸리티 타입으로 충분하면 그쪽이 더 낫다
Partial, Pick, Omit, Readonly로 충분한 문제를 굳이 새 mapped type으로 다시 쓰면 오히려 독자 부담이 늘 수 있습니다.
5) 목적이 명확할 때만 고급 타입 조작을 드러낸다
mapped type과 remapping은 라이브러리/유틸 설계에 강력하지만, 앱 코드 전체에 과하게 퍼지면 타입 오류 메시지까지 어려워질 수 있습니다.
언제 키를 바꿀까
- 기존 객체 타입 전체를 변형한다: mapped type
- readonly/optional modifier를 조정한다: mapped type modifier
- 키 이름 자체를 바꾼다: key remapping
as - 표준 유틸리티로 충분하다: 내장 타입 우선
- 고급 타입 조작이 많아진다: 가독성 다시 점검
주의할 점
mapped type과 remapping을 과하게 중첩하면 타입은 강력해져도 사람이 읽기 어려워질 수 있습니다. 먼저 표준 유틸리티 타입으로 충분한지 확인하는 편이 좋습니다.
// 먼저 Partial, Pick, Omit 같은 표준 도구로 충분한지 확인참고 링크
1 sources