---
id: typescript-advanced-types
title: "TypeScript 고급 타입"
category: "Programming_Language"
status: "draft"
verification_status: "applied"
canonical_id: ""
aliases: ["제네릭", "Generics", "타입 가드", "discriminated union", "유틸리티 타입", "Partial", "Omit"]
duplicate_of: ""
source_trust_level: "A"
confidence_score: 0.94
created_at: 2026-06-13
updated_at: 2026-06-13
review_reason: ""
merge_history: []
tags: ["typescript", "language", "generics", "type-guard", "advanced", "connectai"]
raw_sources: ["ConnectAI/src/features/_shared/eventSourcedStore.ts", "ConnectAI/src/core/queue.ts", "ConnectAI/src/features/providers/types.ts", "ConnectAI/src/memory/index.ts", "ConnectAI/src/intelligence/criticAgent.ts"]
applied_in: ["ConnectAI"]
github_commit: ""
---

# [[TypeScript 고급 타입]]

## 🎯 한 줄 통찰 (One-line insight)
제네릭·판별 유니온·타입 가드·유틸리티 타입은 "중복을 없애면서도 타입 안전을 유지"하는 도구이며, ConnectAI 는 이들로 **하나의 제네릭 스토어가 4개 도메인을 안전하게 처리**하고 **함수 결과를 성공/실패로 명확히 분기**한다 [S1].

## 🧠 핵심 개념 (Core concepts)
1. **제네릭 (Generics):** `createEventStore<E>(...)` 처럼 타입을 파라미터로 받아, 호출부가 `CustomerEvent`/`HireEvent` 등 구체 타입을 끼워 넣으면 그 타입으로 동작한다. 코드는 한 벌, 타입은 N 벌 [S1].
2. **판별 유니온 (Discriminated union):** 공통 *판별 필드* 로 갈래를 나눈 union. `{ ok: true; filePath } | { ok: false; error }` 처럼 `ok` 를 검사하면 컴파일러가 나머지 필드를 자동으로 좁힌다 [S1].
3. **타입 가드 (Type guard):** 반환 타입을 `x is T` 로 선언한 함수. `validate: (e: unknown) => e is E` 가 true 면 컴파일러가 그 블록에서 `e` 를 `E` 로 취급한다 — 런타임 검증과 컴파일 타입 좁히기를 동시에 [S1].
4. **유틸리티 타입 (Utility types):** `Partial<T>`(모든 필드 옵셔널), `Omit<T, K>`(특정 키 제외), `Record<K, V>`(키-값 맵), `Pick<T, K>`. 기존 타입에서 파생 타입을 *연산* 으로 만든다 [S2][S3].
5. **`keyof` / 인덱스 시그니처:** 객체 키 집합을 타입으로 (`Record<string, number>`), 동적 키 접근을 안전하게.

## 🧩 추출된 패턴 (Extracted patterns)
- **제네릭 팩토리 + 검증 주입:** `createEventStore<E>({ relPath, validate })` — 타입 파라미터와 런타임 검증을 함께 받아 I/O 를 추상화하고 도메인 로직은 호출부에 남긴다 [S1].
- **결과를 판별 유니온으로:** 예외를 던지는 대신 `{ ok: true } | { ok: false; error }` 를 반환해 호출부가 분기를 강제로 처리하게 한다 (Rust 의 `Result` 와 유사) [S1].
- **`Partial<T>` 로 설정 병합:** 생성자가 `config?: Partial<MemoryConfig>` 를 받아 기본값과 스프레드 병합 (`{ ...defaults, ...config }`) — 부분 오버라이드 패턴 [S4].
- **`Omit<T, 'model'>` 로 파라미터 일부만:** `streamCloudCompletion` 이 `Omit<StreamParams, 'model'>` 을 받아, model 은 내부에서 채운다 — "이미 아는 필드는 받지 않는다" [S3].
- **함수 타입 별칭으로 의존성 주입:** `type CritiqueLlmCall = (system, user, maxTokens) => Promise<string>` — LLM 호출을 타입으로 추상화해 모듈을 순수하게 유지 [S5].

## 📖 세부 내용 (Details)
### 제네릭의 실전 가치
ConnectAI 는 customers/hire/runway/feedback 4개 스토어가 같은 CRUD 패턴을 반복해 ~240줄 중복이 있었다. 이를 `createEventStore<E>` 하나로 통합 — 타입 파라미터 `E` 덕분에 각 스토어는 자기 이벤트 타입을 그대로 유지하면서 코드는 공유한다. 제네릭이 없었다면 `any` 로 타입을 잃거나 4벌을 복붙해야 했다 [S1].

```typescript
export interface EventStore<E> {
    read(): E[];
    append(event: E): { ok: true; filePath: string } | { ok: false; error: string };
    count(): number;
}
export function createEventStore<E>(opts: EventStoreOptions<E>): EventStore<E> { /* ... */ }
```

### 판별 유니온으로 안전한 분기
`append` 의 반환값을 보면 호출부는 반드시 `ok` 를 먼저 검사해야 한다:
```typescript
const r = store.append(ev);
if (r.ok) { use(r.filePath); }   // 이 블록에서 r.error 는 타입상 존재하지 않음
else { show(r.error); }          // 이 블록에서 r.filePath 는 존재하지 않음
```
컴파일러가 갈래별로 접근 가능한 필드를 제한하므로, "성공 경로에서 error 를 읽는" 류의 버그가 원천 차단된다.

### 타입 가드 = 런타임 검증 + 컴파일 좁히기
```typescript
validate: (e: unknown) => e is CustomerEvent  // 호출부 시그니처
// 내부에서
if (opts.validate(parsed)) out.push(parsed);  // parsed 가 여기서 E 로 좁혀짐
```
`unknown` 으로 받은 외부 JSON 을 가드 통과 후에만 `E[]` 로 쌓는다 — type erasure 로 런타임에 타입이 없는 한계를 가드로 메운다.

### 제네릭 제약과 기본 타입 파라미터
`enqueue<T>(task: () => Promise<T>): Promise<T>` 처럼 메서드 단위 제네릭도 흔하다. 태스크의 반환 타입 `T` 가 그대로 큐의 반환 타입으로 흐른다 [S2].

## ⚖️ 비교 및 선택 기준 (Comparison & decision criteria)

| 항목 (Option) | 장점 | 단점 | 언제 선택 |
|---|---|---|---|
| 예외 throw | 작성 간단, 호출부 코드 짧음 | 호출부가 처리를 잊기 쉬움 | 진짜 예외적 상황(I/O 실패) |
| 판별 유니온 결과 | 호출부가 분기를 강제 처리 | 보일러플레이트 증가 | 실패가 정상 흐름의 일부일 때 (`append`) |
| 제네릭 함수 | 타입 안전 + 중복 제거 | 시그니처 복잡해질 수 있음 | 같은 로직이 여러 타입에 반복될 때 |
| `any` 캐스팅 | 즉시 통과 | 타입 안전 상실 | 최후의 수단, 외부 라이브러리 경계 |

## ⚖️ 모순 및 업데이트 (Contradictions & updates)
- **과도한 제네릭은 독:** 타입 파라미터가 3개를 넘거나 조건부 타입이 중첩되면 가독성이 급락한다. ConnectAI 는 대부분 단일 `<E>`/`<T>` 수준으로 절제한다.
- **결과 유니온 vs 예외:** ConnectAI 는 둘을 혼용한다 — 파일 append 같은 "흔한 실패"는 유니온, 트랜잭션 위반 같은 "계약 위반"은 커스텀 예외(throw). 일관 규칙은 "호출부가 정상적으로 마주칠 실패면 유니온".

## 🛠️ 적용 사례 (Applied in summary)
- `ConnectAI/src/features/_shared/eventSourcedStore.ts` — 제네릭 + 타입 가드 + 판별 유니온이 한 파일에 모두 [S1].
- `ConnectAI/src/memory/index.ts` — `constructor(brainPath, config?: Partial<MemoryConfig>)` + `{ ...defaults, ...config }` 병합 [S4].
- `ConnectAI/src/features/providers/index.ts` — `Omit<StreamParams, 'model'>` 로 일부 필드만 받는 함수 [S3].

## 💻 코드 패턴 (Code patterns)
```typescript
// 1) 제네릭 팩토리 + 타입 가드 주입 (src/features/_shared/eventSourcedStore.ts)
export interface EventStoreOptions<E> {
    relPath: string;
    validate: (e: unknown) => e is E;   // 타입 가드: 런타임 검증 + 컴파일 좁히기
}
export function createEventStore<E>(opts: EventStoreOptions<E>): EventStore<E> { /* ... */ }
// 사용처:
const store = createEventStore<CustomerEvent>({
    relPath: '.astra/customers.jsonl',
    validate: (e): e is CustomerEvent =>
        typeof (e as any).id === 'string' && typeof (e as any).customerId === 'string',
});

// 2) 판별 유니온 결과 — 호출부가 분기 강제
function append(event: E): { ok: true; filePath: string } | { ok: false; error: string } {
    try { /* write */ return { ok: true, filePath }; }
    catch (e: any) { return { ok: false, error: e?.message || String(e) }; }
}

// 3) Partial<T> 로 부분 설정 병합 (src/memory/index.ts)
constructor(brainPath: string, config?: Partial<MemoryConfig>) {
    this.config = { enabled: true, shortTermLimit: 8, /* ...defaults */ ...config };
}

// 4) 함수 타입 별칭으로 의존성 주입 (src/intelligence/criticAgent.ts)
export type CritiqueLlmCall = (system: string, user: string, maxTokens: number) => Promise<string>;
```

## ✅ 검증 상태 및 신뢰도
- **상태:** draft
- **검증 단계:** applied (ConnectAI 1차 코드에서 모든 패턴 확인)
- **출처 신뢰도:** A
- **신뢰 점수:** 0.94
- **중복 검사 결과:** 신규 생성 (New discovery)

## 🔗 지식 그래프 (Knowledge Graph)
- **상위/루트:** [[TypeScript 기초와 타입 시스템]]
- **관련 개념:** [[이벤트 소싱 스토어 패턴]], [[에러 처리와 커스텀 에러]], [[의존성 주입과 서비스 인터페이스]]
- **참조 맥락:** 로컬 LLM 이 중복 제거·안전한 결과 반환·검증 코드를 작성할 때 참조.

## 📚 출처 (Sources)
- [S1] ConnectAI/src/features/_shared/eventSourcedStore.ts — 제네릭, 타입 가드, 판별 유니온 결과
- [S2] ConnectAI/src/core/queue.ts — 메서드 제네릭 `enqueue<T>`
- [S3] ConnectAI/src/features/providers/index.ts, types.ts — Omit, union literal, ReadonlyArray
- [S4] ConnectAI/src/memory/index.ts — Partial<T> + 스프레드 병합
- [S5] ConnectAI/src/intelligence/criticAgent.ts — 함수 타입 별칭(의존성 주입)

## 📝 변경 이력 (Change history)
- 2026-06-13: ConnectAI 코드 분석 기반 초안 생성.
