SHIN
TypeScript 실전 팁 10선
10편매핑 타입은 기존 타입의 각 프로퍼티를 순회하며 새로운 타입을 만드는 강력한 도구입니다.
// [K in keyof T]: 타입 T의 모든 키를 순회
type MyReadonly<T> = {
readonly [K in keyof T]: T[K]
}
type MyPartial<T> = {
[K in keyof T]?: T[K]
}
type MyRequired<T> = {
[K in keyof T]-?: T[K] // ? 제거 (Required)
}// 모든 값을 string으로
type Stringify<T> = {
[K in keyof T]: string
}
// 모든 값을 배열로
type Arrayify<T> = {
[K in keyof T]: T[K][]
}
interface Point { x: number; y: number }
type StringPoint = Stringify<Point> // { x: string; y: string }
type ArrayPoint = Arrayify<Point> // { x: number[]; y: number[] }TypeScript 4.1+ 에서 as로 키를 변환할 수 있습니다.
// 모든 키에 get 접두사 추가
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface User { name: string; age: number }
type UserGetters = Getters<User>
// {
// getName: () => string
// getAge: () => number
// }// string 값만 가진 키 추출
type StringKeys<T> = {
[K in keyof T]: T[K] extends string ? K : never
}[keyof T]
interface Mixed {
id: number
name: string
email: string
active: boolean
}
type StrKeys = StringKeys<Mixed> // 'name' | 'email'
// 활용: 문자열 필드만 있는 타입
type StringFields<T> = Pick<T, StringKeys<T>>
type MixedStrings = StringFields<Mixed> // { name: string; email: string }// 이벤트 맵에서 on-핸들러 타입 자동 생성
type EventHandlers<T extends Record<string, any>> = {
[K in keyof T as `on${Capitalize<string & K>}`]?: (event: T[K]) => void
}
interface AppEvents {
click: { x: number; y: number }
submit: { formData: FormData }
resize: { width: number; height: number }
}
type Handlers = EventHandlers<AppEvents>
// {
// onClick?: (event: { x: number; y: number }) => void
// onSubmit?: (event: { formData: FormData }) => void
// onResize?: (event: { width: number; height: number }) => void
// }type FormState<T> = {
values: T
errors: Partial<Record<keyof T, string>>
touched: Partial<Record<keyof T, boolean>>
dirty: boolean
isSubmitting: boolean
}
interface LoginForm {
email: string
password: string
}
type LoginFormState = FormState<LoginForm>
// values.email, errors.email, touched.email 등이 모두 타입 안전type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}
const config: DeepReadonly<{ db: { host: string; port: number } }> = {
db: { host: 'localhost', port: 5432 }
}
config.db.host = 'prod' // ❌ 깊은 곳까지 읽기 전용매핑 타입은 반복적인 타입 선언을 자동화하고, 코드 변경 시 타입이 자동으로 업데이트되게 해줍니다.