Ch.15 TypeScript 기초 — AI가 이해하는 타입
제네릭과 유틸리티 타입
같은 함수를 숫자용, 문자용 따로 만들어야 하나?
배열에서 첫 번째 요소를 꺼내는 함수를 만들었습니다. 숫자 배열용, 문자 배열용 따로 만들어야 할까요?
타입만 다른 함수를 하나로 합칠 수 없을까?
제네릭 — 타입을 변수처럼 사용해서, 하나의 함수로 모든 타입을 처리합니다.
핵심 내용
제네릭 = 타입의 변수 <T>로 나중에 결정할 타입을 넣습니다
// 이미 쓰고 있던 제네릭들
const nums: Array<number> = [1, 2, 3];
const p: Promise<string> = fetch("/api")
.then(res => res.text());
const map: Map<string, number> = new Map();
map.set("score", 100);<T>는 관례적 이름 T = Type, K = Key, V = Value, E = Element 아무 이름이나 쓸 수 있지만, 관례를 따르면 읽기 쉽습니다.
TypeScript가 기본 제공하는 유틸리티 타입으로 타입을 변환합니다
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial<T> — 모든 필드를 선택적으로
type UpdateUser = Partial<User>;
// { id?: number; name?: string; ... }
// Pick<T, K> — 특정 필드만 선택
type UserName = Pick<User, "name" | "email">;
// { name: string; email: string }
// Omit<T, K> — 특정 필드 제외
type CreateUser = Omit<User, "id">;
// { name: string; email: string; age: number }// Record<K, V> — 키-값 매핑
type Scores = Record<string, number>;
const scores: Scores = {
math: 90,
english: 85,
science: 92,
};
// 실전: 상태 관리
type LoadingState = Record<
"users" | "posts" | "comments",
boolean
>;React 컴포넌트에서도 제네릭을 활용할 수 있습니다
// 제네릭 리스트 컴포넌트
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, i) => (
<li key={i}>{renderItem(item)}</li>
))}
</ul>
);
}// 사용 — 타입 자동 추론!
interface Product {
id: number;
name: string;
price: number;
}
<List
items={products} // Product[]
renderItem={(item) => (
// item은 자동으로 Product 타입!
<span>{item.name} — {item.price}원</span>
)}
/>// API 응답 래핑 패턴
interface ApiResponse<T> {
data: T;
error: string | null;
loading: boolean;
}
// 재사용 가능한 fetch 훅
function useApi<T>(url: string): ApiResponse<T> {
// ... 구현
}
// 사용
const { data, error } = useApi<Product[]>("/api/products");
// data는 Product[] 타입!바이브 코더 팁: AI에게 제네릭 컴포넌트를 요청할 때 "어떤 타입이든 받을 수 있는 리스트 컴포넌트"라고 하면 제네릭을 활용한 유연한 코드를 생성해줍니다.
React에서 TypeScript를 쓰면 Props, 이벤트, 상태까지 타입으로 안전하게 관리됩니다
// Props 타입 정의
interface ButtonProps {
label: string;
variant?: "primary" | "secondary";
onClick: () => void;
disabled?: boolean;
}
function Button({ label, variant = "primary", onClick, disabled }: ButtonProps) {
return (
<button className={variant} onClick={onClick} disabled={disabled}>
{label}
</button>
);
}// 이벤트 타입
function SearchInput() {
const [query, setQuery] = useState<string>("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log("검색:", query);
};
return (
<form onSubmit={handleSubmit}>
<input value={query} onChange={handleChange} />
</form>
);
}바이브 코더 팁: AI에게 React 코드를 만들어달라고 할 때 "TypeScript로 Props 타입도 같이 만들어줘"라고 하면 자동완성과 에러 체크가 되는 안전한 코드를 받을 수 있습니다.
`Partial<User>`의 결과는?
`function first<T>(arr: T[]): T`에서 T는 함수 호출 시 자동으로 추론된다.
`Omit<User, "id" | "age">`의 결과로 남는 필드는? (User = {id, name, email, age})
Record<string, number>는 모든 키가 string이고 모든 값이 number인 객체를 뜻한다.
핵심 용어
Partial<T>
모든 필드를 선택적(?)으로 만듦 — 수정 폼에 유용
Pick<T, K>
지정한 필드만 골라서 새 타입 생성
Omit<T, K>
지정한 필드를 제외한 새 타입 생성
Record<K, V>
키-값 형태의 객체 타입 생성
React.ChangeEvent<HTMLInputElement>
input 값 변경
React.FormEvent<HTMLFormElement>
폼 제출
React.MouseEvent<HTMLButtonElement>
버튼 클릭
React.KeyboardEvent<HTMLInputElement>
키보드 입력
정리 노트
제네릭과 유틸리티 타입 총정리
핵심 개념
- 제네릭 <T>
- 타입을 매개변수로 받아 재사용 가능한 코드 작성
- Partial<T>
- 모든 속성을 선택적(optional)으로 변환
- Pick<T, K>
- 특정 속성만 골라서 새 타입 생성
- Record<K, V>
- 키 타입 K, 값 타입 V인 객체 타입 생성
유틸리티 타입은 기존 타입을 변환할 때 코드 중복을 줄여줍니다.
퀴즈와 인터랙션으로 더 깊이 학습하세요
play_circle인터랙티브 레슨 시작