들어가며
타입스크립트는 기본적인 타입 외에도 고급 타입 시스템을 제공하여 유연하고 확장성 있는 코드를 작성할 수 있도록 돕습니다. 이번 글에서는 다음 주요 개념들을 다룹니다:
- 제네릭(Generic) : 재사용 가능한 타입 정의
- 매핑 타입(Mapped Types) : 타입 변환 및 생성
- 조건부 타입(Conditional Types) : 타입 수준에서의 논리 처리
이를 통해 복잡한 데이터 구조와 다양한 상황에 적합한 타입을 다룰 수 있습니다.
1. 제네릭(Generics)
1-1. 제네릭이란 무엇인가?
제네릭은 데이터의 타입을 고정하지 않고, 사용하는 시점에서 지정할 수 있는 재사용 가능한 타입 정의입니다.
[typescript]
function identity<T>(value: T): T {
return value;
}
const num = identity<number>(42); // T를 number로 지정
const str = identity<string>('Hello'); // T를 string으로 지정
1-2. 제네릭 인터페이스
제네릭은 인터페이스에도 적용 가능합니다.
[typescript]
interface Box<T> {
content: T;
}
const stringBox: Box<string> = { content: 'Hello' };
const numberBox: Box<number> = { content: 123 };
1-3. 제네릭 클래스
클래스에서도 제네릭을 사용할 수 있습니다.
[typescript]
class KeyValueStore<K, V> {
private store: Map<K, V> = new Map();
set(key: K, value: V): void {
this.store.set(key, value);
}
get(key: K): V | undefined {
return this.store.get(key);
}
}
const store = new KeyValueStore<string, number>();
store.set('age', 30);
console.log(store.get('age')); // 30
1-4. 제네릭 제약 (Constraints)
제네릭에 특정 조건을 부여할 수도 있습니다.
[typescript]
function printProperty<T, K extends keyof T>(obj: T, key: K): void {
console.log(obj[key]);
}
const person = { name: 'Alice', age: 30 };
printProperty(person, 'name'); // Alice
// printProperty(person, 'address'); // 오류! 'address'는 T에 존재하지 않음
2. 매핑 타입(Mapped Types)
2-1. 기본 매핑 타입
매핑 타입은 기존 타입의 각 속성을 변환하여 새로운 타입을 생성합니다.
[typescript]
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
type User = {
id: number;
name: string;
};
type ReadonlyUser = Readonly<User>;
2-2. 내장 유틸리티 타입 활용
타입스크립트는 다양한 매핑 타입 유틸리티를 제공합니다.
- Partial : 모든 속성을 선택적으로 만듭니다.
[typescript]
type Partial<T> = {
[K in keyof T]?: T[K];
};
const user: Partial<User> = { name: 'Alice' }; // id는 없어도 OK - Pick : 특정 속성만 선택합니다.
[typescript]
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type UserNameOnly = Pick<User, 'name'>; - Omit : 특정 속성을 제외합니다.
[typescript]
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type UserWithoutId = Omit<User, 'id'>; - Record : 모든 키에 특정 타입을 매핑합니다.
[typescript]
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type Config = Record<'host' | 'port', string>;
3. 조건부 타입(Conditional Types)
3-1. 기본 문법
조건부 타입은 삼항 연산자(T extends U ? X : Y) 형태로 사용됩니다.
[typescript]
type IsString<T> = T extends string ? 'Yes' : 'No';
type A = IsString<string>; // 'Yes'
type B = IsString<number>; // 'No'
3-2. 분배적 조건부 타입
유니온 타입에서 조건부 타입은 각 요소에 개별적으로 적용됩니다.
[typescript]
type Exclude<T, U> = T extends U ? never : T;
type Result = Exclude<'a' | 'b' | 'c', 'a' | 'c'>;
// Result = 'b'
3-3. 활용 예제
조건부 타입을 활용하면 복잡한 타입 로직을 간결하게 처리할 수 있습니다.
[typescript]
type Flatten<T> = T extends any[] ? T[number] : T;
type A = Flatten<number[]>; // number
type B = Flatten<string>; // string
4. 실용 예제 : 고급 타입 활용
문제
전자상거래 시스템에서 상품 데이터를 관리한다고 가정합니다. 상품은 다음과 같은 구조를 가집니다:
- id : 상품 ID (숫자)
- name : 상품 이름 (문자열)
- details : 선택적 세부 정보 (객체)
데이터를 처리하는 함수를 작성하며, 고급 타입을 사용해 코드를 안전하고 유연하게 작성합니다.
코드
[typescript]
type Product = {
id: number;
name: string;
details?: {
description: string;
price: number;
};
};
type ProductSummary = Pick<Product, 'id' | 'name'>;
function getProductSummary(product: Product): ProductSummary {
const { id, name } = product;
return { id, name };
}
function printProductDetails<T extends Product>(product: T): void {
console.log(`Product: ${product.name}`);
if (product.details) {
console.log(`Price: $${product.details.price}`);
}
}
const product: Product = {
id: 1,
name: 'Laptop',
details: {
description: 'A powerful laptop',
price: 1500,
},
};
console.log(getProductSummary(product));
printProductDetails(product);
마무리
이번 글에서는 타입스크립트의 고급 타입인 제네릭, 매핑 타입, 조건부 타입을 학습했습니다. 이 기능들은 복잡한 데이터 구조를 처리하거나 재사용 가능한 타입 로직을 작성할 때 매우 유용합니다. 고급 타입을 활용하면 코드의 안전성과 가독성을 동시에 확보할 수 있습니다.
다음 단계에서는 모듈과 네임스페이스, 선언 병합을 다루어, 대규모 코드베이스에서도 유지보수와 확장성을 고려한 구조화 방법을 학습하겠습니다.
'프로그래밍 > Typescript' 카테고리의 다른 글
[Typescript] 인터페이스와 클래스 : 구조적 타이핑과 객체지향 패턴 (1) | 2024.12.20 |
---|---|
[Typescript] 타입 시스템 이해 : 정적 타입으로 안전한 코드 작성 (1) | 2024.12.18 |
[Typescript] 타입스크립트 기초 입문 : 타입스크립트를 시작하기 위한 첫걸음 (2) | 2024.12.15 |