---
id: wiki-2026-0508-타입-안전성-type-safety
title: 타입 안전성 (Type Safety)
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Type Safety, Strong Typing, Sound Typing]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [type-system, correctness, typescript, rust, soundness]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: polyglot
  framework: typescript-rust-haskell
---

# 타입 안전성 (Type Safety)

## 매 한 줄
> **"매 ill-typed program 의 X-run"**. Type safety 는 type system 이 program execution 동안 type error (e.g., string 에 number method 호출) 가 발생하지 않음을 guarantee 하는 property. **Static** type safety (Rust, Haskell, OCaml) 는 compile time 에 reject, **Dynamic** (Python, JS) 는 runtime 에 throw, **Gradual** (TypeScript, Python+mypy) 는 매 mix. 2026 의 trend 는 매 sound + gradual 의 confluence.

## 매 핵심

### 매 type safety 의 차원
- **Soundness**: well-typed program 의 type error 의 X. Rust/Haskell/Idris 의 sound. TypeScript 의 unsound (intentional — 매 ergonomic trade-off).
- **Completeness**: type-correct program 의 모두 의 accept. 매 undecidable 한 trade-off.
- **Strong vs Weak**: strong = no implicit coercion (Python), weak = implicit (JS `'5' + 3 === '53'`).
- **Static vs Dynamic**: check time.
- **Manifest vs Inferred**: annotation 의 explicit 여부.

### 매 unsoundness escape hatches
- TS: `any`, `as`, `// @ts-ignore`, function bivariance, `Object.assign`.
- Python: `Any`, `cast`, missing annotations.
- Rust: `unsafe` block — 매 explicit, 매 audit-able.

### 매 응용
1. Refactoring confidence — rename 의 자동 propagate.
2. API contract — request/response shape 의 enforce.
3. Bug prevention — null deref, off-by-one type error 의 catch.
4. Self-documenting code — type 의 living spec.

## 💻 패턴

### Branded types (nominal typing in structural system)
```typescript
type UserId = string & { readonly __brand: 'UserId' }
type OrderId = string & { readonly __brand: 'OrderId' }

function makeUserId(s: string): UserId {
  return s as UserId  // single trusted constructor
}

function getUser(id: UserId) { /* ... */ }

const u = makeUserId('u_abc')
const o = 'o_123' as OrderId
getUser(u)   // OK
getUser(o)   // ERROR — different brand
```

### Exhaustive switch (compile-time correctness)
```typescript
type Status = 'pending' | 'active' | 'archived'

function label(s: Status): string {
  switch (s) {
    case 'pending':  return '대기'
    case 'active':   return '활성'
    case 'archived': return '보관'
    default: {
      const _: never = s
      throw new Error('unreachable')
    }
  }
}
// Adding 'deleted' to Status → compile error here, not runtime crash
```

### Result type (no exception)
```typescript
type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E }

async function fetchUser(id: string): Promise<Result<User>> {
  try {
    const r = await fetch(`/u/${id}`)
    if (!r.ok) return { ok: false, error: new Error(`HTTP ${r.status}`) }
    return { ok: true, value: await r.json() }
  } catch (e) {
    return { ok: false, error: e as Error }
  }
}
```

### Schema-driven I/O (runtime + static)
```typescript
import { z } from 'zod'

const ApiUser = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  age: z.number().int().min(0),
})
type ApiUser = z.infer<typeof ApiUser>

async function getUser(id: string): Promise<ApiUser> {
  const raw = await fetch(`/u/${id}`).then(r => r.json())
  return ApiUser.parse(raw)  // throws on shape mismatch
}
```

### Rust — soundness via ownership
```rust
fn main() {
    let s = String::from("hello");
    let r1 = &s;
    let r2 = &s;          // OK — multiple immutable borrows
    println!("{r1} {r2}");

    let m = &mut s;       // ERROR — can't borrow mutable while immutable exists
}
```

### Python — gradual typing with mypy/pyright strict
```python
# pyproject.toml
[tool.pyright]
strict = ["src"]
reportMissingTypeStubs = "error"

# code
from typing import Literal

def label(s: Literal["pending", "active", "archived"]) -> str:
    match s:
        case "pending":  return "대기"
        case "active":   return "활성"
        case "archived": return "보관"
    # mypy/pyright catches missing case
```

### TS strict mode (essential 2026 baseline)
```json
// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,    // arr[i] is T | undefined
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true,
    "noFallthroughCasesInSwitch": true
  }
}
```

### `unknown` over `any`
```typescript
// BAD
function parse(json: string): any {
  return JSON.parse(json)
}

// GOOD
function parse(json: string): unknown {
  return JSON.parse(json)  // forces caller to narrow
}

const data = parse('{"x":1}')
// data.x  // ERROR
if (typeof data === 'object' && data !== null && 'x' in data) {
  data.x  // OK
}
```

### Enforced phantom state (typestate pattern)
```typescript
type Builder<State extends 'empty' | 'has-name' | 'ready'> = {
  setName(n: string): Builder<'has-name'>
  setEmail(e: string): Builder<'ready'>  // assume after name
  build(): User                          // only on 'ready'
}
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| 신규 TS 프로젝트 | `strict: true` + `noUncheckedIndexedAccess` |
| Python | pyright strict, mypy strict |
| Critical / safety | Rust, OCaml, Idris (sound) |
| API boundary | Schema (Zod, Pydantic) — runtime + static |
| ID confusion 방지 | Branded types |
| Error 의 throw 회피 | `Result<T, E>` |
| Interop with untyped | `unknown` + narrow (NOT `any`) |
| Performance + safety | Rust |

**기본값**: Static + sound (Rust) 또는 Static + gradual strict (TS strict, Python pyright strict). API boundary 의 schema. `any` 의 ban.

## 🔗 Graph
- 부모: [[TypeScript 타입 시스템 (TypeScript Type System)|Type System]]
- 응용: [[TypeScript]] · [[Zod]]
- Adjacent: [[Branded Types]] · [[Type Narrowing]] · [[Result Type]]

## 🤖 LLM 활용
**언제**: 매 generated code 의 strict mode 의 compile 확인. 매 LLM 의 `any` cast 의 propose 의 reject — `unknown` + narrow 의 demand.
**언제 X**: 매 prototype 의 throwaway script 의 over-engineer X — 매 production path 의 only-strict.

## ❌ 안티패턴
- **`any` proliferation**: TS 의 `any` 의 single use 의 entire chain 의 type 의 lose.
- **`as` 의 lying cast**: `data as User` without verification.
- **String IDs everywhere**: branded types 의 X-use → wrong-ID-as-arg bug.
- **Throw-everything**: Result type 또는 typed error 의 X-use.
- **Loose schema**: Zod `.passthrough()` 의 abuse — unknown fields 의 leak.

## 🧪 검증 / 중복
- Verified (TypeScript docs, Rust Book 2026, Pierce — Types and Programming Languages).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — soundness, branded types, Result, schema, strict configs |
