유틸리티 타입 (1)
유틸리티 타입?
"TypeScript는 공통 타입 변환을 용이하게 하기 위해 몇 가지 유틸리티 타입을 제공합니다."
Partial
Partial<T>
는 타입 T
의 모든 프로퍼티를 선택적으로 만드는 타입을 구성합니다.
type Partial<T> = {
[P in keyof T]?: T[P];
};
Partial<T>
는 특정 타입 T의 모든 프로퍼티에 ?
를 붙여 선택적 프로퍼티로 만듭니다.
interface IData {
title: string;
timestamp: number;
description: string;
tags: tag[]
}
function updateData(currentData: IData, newData: ?) {
update({
...currentData,
...newData,
timestamp: getTimestamp(),
});
}
일반적으로 업데이트 함수는 기존 데이터의 특정 부분을 변경합니다.
updateData
함수는 newData
를 인자로 받아 해당 데이터를 타임스탬프와 함께 업데이트합니다.
스프레드 연산자를 사 용한 것으로 보아 newData가 어떤 프로퍼티를 가지는 지 확실하지 않습니다.
title
을 업데이트 할 수도, description
이나 tags
를, 또는 셋 모두 업데이트 할 수도 있습니다.
따라서 이 경우, IData
의 모든 프로퍼티를 옵셔널로 만드는 Partial
유틸리티 타입이 적합합니다.
Partial<IData>
{
title?: string;
timestamp?: number;
description?: string;
tags?: tag[]
}
Required
Partial<T>
와 반대로 모든 프로퍼티를 필수로 설정하는 타입을 만드는 Required<T>
유틸리티 타입도 있습니다.
interface IData {
title?: string;
description?: string;
}
const a: IData = { title: "some title" }; // description이 없어도 OK.
const b: Required<IData> = { title: "some title" }; // 오류: 프로퍼티 description이 없습니다.
Omit
Omit<T, K>
은 타입 T의 프로퍼티 중 K를 제거한 타입을 구성합니다.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
T
의 프로퍼티를 인자로 받으며(K extends keyof T
), T
프로퍼티 중 K
를 제외한(Pick<T, Exclude<keyof T, K>>
) 새로운 타입을 구성합니다.
퀴즈의 updateData
함수는 newData
프로퍼티가 명확하지 않아도 범용적으로 사용할 수 있는 함수로 특정 프로퍼티를 제외하는 Omit<T, K>
는 적합하지 않습니다.
추상화 레벨이 한 단계 높아진다면 사용할 수는 있습니다. 아래와 같이요.
function updateTitle(
currentData: IData,
newData: Omit<IData, "description" | "tags">
) {
update({
...currentData,
...newData,
});
}
하지만 위와 같이 프로퍼티를 특정해 어떤 동작을 수행하는 경우, IData
가 확장될 경우 의도하지 않은 속성이 같이 업데이트 될 위험이 있습니다.
newData.title
을 특정해 title
을 업데이트 할 수도 있겠지만 위 경우 대상 프로퍼티를 정의해 주는 것이 논리적으로 적절합니다.
이럴 경우에 Pick<T, K>
을 사용합니다.
Pick
Pick<T, K>
은 타입 T
에서 특정 프로퍼티를 선택해 새로운 타입을 구성합니다.
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
두 번째 인자 K
로 T
타입의 프로퍼티를 받으며, K
를 프로퍼티로 가지는 새로운 타입을 구성합니다.
위의 updateTitle()
에 아래와 같이 적용할 수 있습니다.
function updateTitle(currentData: IData, newData: Pick<IData, "title">) {
update({
...currentData,
...newData,
});
}
Record
Record<K, T>
는 첫 번째 인자 타입 K를 프로퍼티로, 해당 프로퍼티의 타입으로 T를 가지는 새로운 객체 타입을 구성합니다.
type Record<K extends keyof any, T> = {
[P in K]: T;
};
아래와 같이 사용할 수 있습니다.
type dataState = "pending" | "fulfilled" | "error"
const obj: Record<dataState, IData[]> = {
pending: IData 타입의 객체 배열,
fulfilled: IData 타입의 객체 배열,
error: IData 타입의 객체 배열,
}
Partial
과의 콤비네이션도 가넝.
type dataState = "pending" | "fulfilled" | "error"
const obj: Partial<Record<dataState, IData[]>> = {
pending: IData 타입의 객체 배열
} // fulfilled, error 프로퍼티가 없어도 OK.