매핑 타입(Mapped Types)
매핑 타입(Mapped Types)은 기존 타입을 변환하여 새로운 타입을 만들 수 있는 유틸리티 타입 중 하나다. 매핑 타입을 사용하면 기존 타입의 속성 이름, 타입 등을 변경하여 새로운 타입을 만들 수 있다.
매핑 타입의 구조는 아래와 같다.
{ [P in K]: T }
위 구조에서 P는 속성 이름의 타입, K는 P에 포함될 속성 이름의 타입을 의미하며, T는 속성의 타입을 나타낸다.
interface Person {
name: string;
age: number;
location: string;
}
위 Person 인터페이스를 매핑 타입을 사용하여 다음과 같이 변환할 수 있다.
type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };
위 코드에서 keyof Person은 Person 인터페이스의 모든 속성 이름을 union 타입으로 만든다. readonly는 해당 속성이 읽기 전용임을 나타내며, Person[P]는 Person 인터페이스에서 해당 속성의 타입을 가져온다. 따라서 ReadonlyPerson은 다음과 같이 정의된다.
type ReadonlyPerson = {
readonly name: string;
readonly age: number;
readonly location: string;
}
이렇게 매핑 타입을 사용하면 기존 타입을 보다 유연하게 변환할 수 있으며, 이를 통해 코드의 재사용성과 가독성을 높일 수 있다.
또한 다양한 상황에서 유용하게 사용될 수 있다.
모든 속성의 타입을 읽기 전용으로 변경하기
Readonly<T> 타입을 사용하여 기존 타입에서 모든 속성의 타입을 읽기 전용으로 변경하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 모든 속성의 타입을 읽기 전용으로 변경하여 PersonWithReadonlyProps 타입을 만든다.
type PersonWithReadonlyProps = Readonly<Person>;
모든 속성의 타입을 필수로 변경하기
Required<T> 타입을 사용하여 기존 타입에서 모든 속성의 타입을 필수로 변경하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 모든 속성의 타입을 필수로 변경하여 PersonWithRequiredProps 타입을 만든다.
type PersonWithRequiredProps = Required<Person>;
모든 속성의 타입을 선택적으로 변경하기
Partial<T> 타입을 사용하여 기존 타입에서 모든 속성의 타입을 선택적으로 변경하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 모든 속성의 타입을 선택적으로 변경하여 PersonWithPartialProps 타입을 만든다.
type PersonWithPartialProps = Partial<Person>;
모든 속성의 타입을 지정한 타입으로 변경하기
Record<K, T> 타입을 사용하여 모든 속성의 타입을 지정한 타입으로 변경하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 모든 속성의 타입을 number로 변경하여 PersonWithNumberProps 타입을 만든다.
type PersonWithNumberProps = Record<keyof Person, number>;
객체 속성 타입 변경하기
{ [K in keyof T]: U } 문법을 사용하여 객체의 모든 속성의 타입을 일괄적으로 변경할 수 있다.
아래 예시에서는 Person 객체의 모든 속성의 타입을 string으로 변경하여 PersonWithStringProps 타입을 만든다.
type PersonWithStringProps = { [K in keyof Person]: string };
속성 이름을 변경하기
{ [K in keyof T as U]: T[K] } 문법을 사용하여 기존 타입의 속성 이름을 변경하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 age 속성을 years로 변경하여 PersonWithRenamedProps 타입을 만든다.
type PersonWithRenamedProps = { [K in keyof Person as K extends 'age' ? 'years' : K]: Person[K] };
속성 값 변경하기
Pick<T, K> 타입과 조합하여 기존 타입의 일부 속성만 선택하여 해당 속성의 타입을 변경할 수 있다.
아래 예시에서는 Person 타입의 age 속성 타입을 string으로 변경하여 PersonWithStringAge 타입을 만든다.
type PersonWithStringAge = Pick<Person, 'name' | 'location'> & { age: string };
타입에서 특정 속성 제거하기
Omit<T, K> 타입을 사용하여 기존 타입에서 특정 속성을 제거하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 location 속성을 제거하여 PersonWithoutLocation 타입을 만든다.
type PersonWithoutLocation = Omit<Person, 'location'>;
타입에서 선택적 속성 제거하기
Exclude<T, U> 타입을 사용하여 기존 타입에서 U에 해당하는 속성을 제거하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 선택적 속성인 location을 제거하여 PersonWithoutOptional 타입을 만든다.
type PersonWithoutOptional = Pick<Person, Exclude<keyof Person, 'location'>>;
타입에서 특정 속성에 조건 추가하기
Exclude<T, U> 타입과 Pick<T, K> 타입을 조합하여 기존 타입에서 특정 속성의 타입을 변경할 수 있다.
아래 예시에서는 Person 타입에서 age 속성을 선택하여, 해당 속성이 number인 경우 string으로 변경하여 PersonWithStringAge 타입을 만든다.
type PersonWithStringAge = Pick<Person, 'name' | 'location'> & { age: Exclude<Person['age'], number> } & { age?: string };
타입에서 undefined 속성 제거하기
NonNullable<T> 타입을 사용하여 기존 타입에서 undefined 값을 가질 수 있는 속성을 제거하여 새로운 타입을 만들 수 있다.
아래 예시에서는 Person 타입에서 age 속성이 undefined 값을 가질 수 있는 경우 제거하여 PersonWithoutUndefined 타입을 만든다.
type PersonWithoutUndefined = { [K in keyof Person]: NonNullable<Person[K]> };
다양한 컬렉션을 표현하는 타입 생성하기
interface Collection<T> {
[key: string]: T;
}
type ReadonlyCollection<T> = Readonly<Collection<T>>;
type OptionalCollection<T> = Partial<Collection<T>>;
type RequiredCollection<T> = Required<Collection<T>>;
type ReadonlyOptionalCollection<T> = Readonly<OptionalCollection<T>>;
type ReadonlyRequiredCollection<T> = Readonly<RequiredCollection<T>>;
위 코드에서 Collection<T> 인터페이스는 key 속성이 문자열이고, 값은 T 타입인 객체를 나타낸다.
매핑 타입을 사용하여 Collection<T>에서 파생된 다른 타입들을 만들어낸다.
- ReadonlyCollection<T>: 모든 속성이 읽기 전용인 컬렉션
- OptionalCollection<T>: 모든 속성이 선택적인 컬렉션
- RequiredCollection<T>: 모든 속성이 필수인 컬렉션
- ReadonlyOptionalCollection<T>: 모든 속성이 읽기 전용이고 선택적인 컬렉션
- ReadonlyRequiredCollection<T>: 모든 속성이 읽기 전용이고 필수인 컬렉션
매핑 타입을 사용하면 이처럼 쉽게 타입을 변환하고 확장할 수 있다. 또한, 타입스크립트에서 기본적으로 제공되는 매핑 타입 이외에도 사용자가 직접 매핑 타입을 정의할 수 있다. 이를 통해 필요에 따라 더욱 편리하고 유연한 타입을 만들어낼 수 있다.