---
id: typescript-type-predicates
title: TypeScript Type Predicates (사용자 정의 타입 가드)
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [typescript, type-guards, narrowing, vibe-coding]
tech_stack: { language: "TypeScript 5+", applicable_to: ["Web", "Backend"] }
applied_in: []
aliases: ["x is T", type guard, narrowing function]
---

# TypeScript Type Predicates

> `function isFoo(x: unknown): x is Foo` — 컴파일러에게 "true 면 Foo 다" 약속. **약속을 깨면 침묵 사고**. `is T` 반환 함수의 본문이 진실 말하는지 책임은 작성자.

## 📖 핵심 개념
- 일반 boolean: `function isFoo(x): boolean` — 호출자 narrowing 안 됨.
- type predicate: `function isFoo(x): x is Foo` — true 가지에서 x 가 Foo 로 좁혀짐.
- false 가지에서는 명시적 `assert x is not T` 도 가능 (asserts).

## 💻 코드 패턴

### 1. 기본
```ts
type Cat = { kind: 'cat'; meow: () => void };
type Dog = { kind: 'dog'; bark: () => void };
type Animal = Cat | Dog;

function isCat(a: Animal): a is Cat {
  return a.kind === 'cat';
}

const a: Animal = ...;
if (isCat(a)) a.meow(); // ✅ a 는 Cat
else a.bark();          // ✅ a 는 Dog (자동 narrowing)
```

### 2. 일반화 — null 제거
```ts
function isPresent<T>(x: T | null | undefined): x is T {
  return x !== null && x !== undefined;
}

const arr: (string | null)[] = ['a', null, 'b'];
const filtered: string[] = arr.filter(isPresent);
```

### 3. assertion 함수
```ts
function assertIsUser(x: unknown): asserts x is User {
  if (!x || typeof x !== 'object' || !('id' in x)) {
    throw new Error('Not a user');
  }
}

const data = await fetch('...').then(r => r.json());
assertIsUser(data);
data.id; // ✅ User 로 좁혀짐
```

### 4. zod 와의 조합
```ts
const UserSchema = z.object({ id: z.string(), email: z.string().email() });
type User = z.infer<typeof UserSchema>;

function isUser(x: unknown): x is User {
  return UserSchema.safeParse(x).success;
}
```

## 🤔 의사결정 기준
| 상황 | predicate | assertion |
|---|---|---|
| 두 가지 타입 구분 후 양 분기 | predicate (`is`) | — |
| 불일치 시 진행 불가, 즉시 종료 | — | assertion (`asserts`) |
| filter / find 결과 타입 좁히기 | predicate | — |
| API 응답 검증 후 도메인 타입 변환 | zod 권장 | assertion 도 OK |

## ❌ 안티패턴
- **predicate 본문이 거짓말**: `function isFoo(x): x is Foo { return true }` — 컴파일러는 믿지만 런타임 사고. 본문이 진실해야.
- **discriminated union 에 predicate 남발**: `if (x.kind === 'cat')` 만으로 narrowing 충분. predicate 불필요.
- **predicate 안에서 side effect**: 호출자는 pure 가정. fetch / setState 금지.
- **`as T` 로 끝**: assertion 함수 안 쓰고 `x as User` — 검증 없음. 사고.
- **assertion 함수가 throw 외 다른 동작**: 약속은 "통과하면 좁혀짐 + 안 통과하면 throw". 다른 동작 금지.

## 🤖 LLM 활용 힌트
- API 응답 다루는 코드에 "type predicate + zod 결합" 명시.
- LLM 이 `as` 캐스팅 남발하면 predicate 변환 요청.

## 🔗 관련 문서
- [[TypeScript_Branded_Types]]
- [[Null_Safety_Patterns]]
