mapped type은 객체에 대한 type을 지정할 때, property key 혹은 value를 조금 더 유연하게 지정하도록 도와준다. 유연하다는 의미는, 마치 Js의 computed property names 문법 처럼 사용할 수 있다는 의미이다. 이를 잘 활용하면 typescript에서 제공하는 type utils를 넘어서 나만의 utils를 만들고 싶을 때, 유용하게 사용할 수 있다.
js compomputed property names 예시
let param = 'size'
let config = {
[param]: 12,
['mobile' + param.charAt(0).toUpperCase() + param.slice(1)]: 4
}
type OnlyBools = {
[key: string]: boolean;
};
interface OnlyBools = {
[key: string]: boolean;
};
// Generic 문법으로 Type(type|interface)을 받아 입력받은 Type과 동일한 key에 원하는 type으로 변경할 수 있다.
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
// -readonly를 통해 readonly를 제거할 수 있도 있다.
type deleteReadonly<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};
// -?를 통해 optional 타입을 제거한다.
type deleteOptional<Type> = {
[Property in keyof Type]-?: Type[Property];
};
type TypePrefixer<Type, S extends string> = {
[Property in keyof Type as `${S}${Capitalize<string & Property>}`]: Type[Property];
};
interface ImageProps
id: string;
src: string;
alt: string;
$width?: string | number;
}
export type AvatarProps = TypePrefixer<Omit<ImageProps, '$width'>, 'image'> & {
src: string;
userName: string;
size?: 'small' | 'medium' | 'large';
};
// 결과
type AvatarProps = {
imageId: string;
imageSrc: string;
imageAlt: string;
src: string;
userName: string;
size?: 'small' | 'medium' | 'large';
}
타입마다 id를 imageId처럼 상세하게 미리 지정할 수도 있지만, Image 컴포넌트를 사용할 때는 굳이 imageId로 key로 설정하지 않더라도 id가 imageId 임을 알 수 있기 때문에 3번 예제처럼 필요한 경우에만 typePrefixer로 key이름을 바꿔주는 방법도 좋은 대안이라고 생각한다.