---
id: wiki-2026-0508-타입-가드-type-predicates
title: 타입 가드 (Type Predicates)
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Type Guard, User-defined type predicate, is-function, asserts function]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [typescript, type-system, narrowing, architecture]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: TypeScript
  framework: TS 5.7+
---

# 타입 가드 (Type Predicates)

## 매 한 줄
> **"매 runtime check 와 매 compile-time narrowing 을 매 동시에 표현하는 함수 signature."**. `value is T` (predicate) 와 `asserts value is T` (assertion function) 의 두 형태. 매 type assertion (`as T`) 의 매 unsafe escape hatch 에 비해 매 verified narrowing — 매 runtime 에 실제로 검증.

## 매 핵심

### 매 Built-in vs User-defined
- **Built-in**: 매 `typeof`, `instanceof`, `in`, equality narrowing. 매 TS 가 자동 인식.
- **User-defined**: 매 `value is T` return type — 매 명시적 contract.

### 매 Predicate vs Assertion
- **Predicate (`is T`)**: 매 boolean return. 매 `if (isUser(x))` 분기 narrowing.
- **Assertion (`asserts is T`)**: 매 void return. 매 throw or pass-through. 매 NodeJS `assert` 와 호환.

### 매 TS 5.5+ 추론 개선
- 매 단순 `return typeof x === 'string'` 등은 매 TS 5.5 부터 매 inferred predicate — 매 명시 X 가능.
- 매 그래도 매 explicit predicate 가 매 documentation 으로 권장.

### 매 응용
1. Discriminated union narrowing.
2. External (JSON / API) data 의 runtime validation gate.
3. Filter + narrowing combo (`array.filter(isUser)` → `User[]`).
4. Class hierarchy narrowing (instanceof 대안).

## 💻 패턴

### Pattern 1 — Basic predicate
```typescript
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function process(input: unknown) {
  if (isString(input)) {
    input.toUpperCase(); // 매 narrowed → string.
  }
}
```

### Pattern 2 — Object shape predicate
```typescript
interface User {
  id: number;
  email: string;
}

function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    typeof (value as Record<string, unknown>).id === 'number' &&
    'email' in value &&
    typeof (value as Record<string, unknown>).email === 'string'
  );
}
```

### Pattern 3 — Discriminated union
```typescript
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; side: number };

function isCircle(s: Shape): s is Extract<Shape, { kind: 'circle' }> {
  return s.kind === 'circle';
}

function area(s: Shape) {
  if (isCircle(s)) return Math.PI * s.radius ** 2;
  return s.side ** 2;
}
// 매 discriminant 가 충분하지만 매 helper 로 가독성 향상.
```

### Pattern 4 — Filter + narrowing
```typescript
const items: (string | null | undefined)[] = ['a', null, 'b', undefined];

function isDefined<T>(v: T | null | undefined): v is T {
  return v !== null && v !== undefined;
}

const clean = items.filter(isDefined);
// type: string[] — 매 (string | null | undefined)[] 의 X.
```

### Pattern 5 — Assertion function
```typescript
function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== 'string') {
    throw new TypeError(`expected string, got ${typeof value}`);
  }
}

function shout(input: unknown) {
  assertIsString(input);
  return input.toUpperCase(); // 매 narrowed without if.
}
```

### Pattern 6 — Zod (runtime + type 자동 동기화)
```typescript
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;

function isUser(value: unknown): value is User {
  return UserSchema.safeParse(value).success;
}
// 매 schema → predicate + type 의 single source of truth.
```

### Pattern 7 — Generic predicate (array element check)
```typescript
function isArrayOf<T>(
  arr: unknown,
  guard: (item: unknown) => item is T,
): arr is T[] {
  return Array.isArray(arr) && arr.every(guard);
}

const data: unknown = ['a', 'b', 'c'];
if (isArrayOf(data, isString)) {
  data.map((s) => s.length); // 매 string[] 으로 narrowed.
}
```

### Pattern 8 — Negation 의 한계
```typescript
function isString(v: unknown): v is string { /* ... */ }

function example(v: string | number) {
  if (!isString(v)) {
    v; // type: number — 매 control-flow narrowing 동작.
  }
}
// 매 그러나 매 `v is not string` syntax 는 매 TS 에 X — negation 은 매 control flow 가 추론.
```

## 매 결정 기준
| 상황 | Tool |
|---|---|
| 매 단일 primitive check | `typeof` (built-in) |
| 매 class instance | `instanceof` (built-in) |
| 매 union discriminant | discriminated union + switch |
| 매 external JSON | zod / valibot + predicate |
| 매 throw on failure | `asserts` function |
| 매 filter + narrow | predicate + `array.filter` |

**기본값**: 매 schema validator (zod) + `z.infer` + predicate 의 조합. 매 raw `as T` 의 회피.

## 🔗 Graph
- 부모: [[TypeScript 타입 시스템]] · [[Control Flow Analysis]]
- 변형: [[Discriminated Unions]] · [[Assertion Functions]] · [[타입 단언(Type Assertions)]]
- 응용: [[Zod 런타임 검증]] · [[API Boundary Validation]]
- Adjacent: [[unknown vs any]] · [[Branded Types]]

## 🤖 LLM 활용
**언제**: 매 unknown / external data 의 narrowing, 매 array filter + type sharpening, 매 custom domain type 의 validation.
**언제 X**: 매 TS 가 이미 narrow 가능한 context (typeof, in 으로 충분). 매 predicate 의 return logic 이 매 type 과 mismatch — 매 silent type lie.

## ❌ 안티패턴
- **Lying predicate**: 매 `return true` 만 — 매 runtime 검증 X 인데 매 type 만 단언.
- **Predicate 안 mutation**: 매 side-effect — 매 `Array.prototype.filter` callback 의 매 contract 위반.
- **`as T` 의존**: 매 predicate 으로 가능한데 매 raw assertion — 매 runtime 안전성 손실.
- **Heavy validation in hot path**: 매 large object 의 매 deep predicate — 매 매 boundary 에서만 1회.

## 🧪 검증 / 중복
- Verified (TypeScript Handbook 2026, TS 5.7 narrowing rules).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — Type Predicates + Assertion Functions + Zod 통합 |
