---
id: ts-type-level-patterns
title: TypeScript Type-level Programming
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [typescript, type-level, vibe-coding]
tech_stack: { language: "TS", applicable_to: ["Frontend", "Backend"] }
applied_in: []
aliases: [type-level programming, conditional types, infer, mapped types, recursive types, type gymnastics]
---

# Type-level Programming

> TS 타입 가 Turing-complete. **Conditional + infer + recursive = 강력**. Library / API design 에서 type-safe end-to-end.

## 📖 핵심 개념
- Type 만 (runtime 0).
- Compile-time 정확.
- 너무 많이 = "type gymnastics" — 가독성 ↓.
- ts-toolbelt / type-fest 가 utility.

## 💻 코드 패턴

### Conditional type
```ts
type IsString<T> = T extends string ? true : false;
type A = IsString<'a'>;        // true
type B = IsString<123>;        // false
```

### Distributive
```ts
type ToArray<T> = T extends any ? T[] : never;
type C = ToArray<string | number>;
// → string[] | number[]
// (각 union member 가 distribute)
```

### `infer`
```ts
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type Fn = () => string;
type R = ReturnType<Fn>;   // string
```

### Mapped type
```ts
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };

type User = { id: string; name: string };
type ReadonlyUser = Readonly<User>;
```

### Key remap
```ts
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type User = { name: string; age: number };
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }
```

### Template literal
```ts
type Hello<T extends string> = `Hello, ${T}`;
type Greet = Hello<'Alice'>;   // 'Hello, Alice'

type Camel<T> = T extends `${infer A}_${infer B}${infer Rest}`
  ? `${A}${Uppercase<B>}${Camel<Rest>}`
  : T;

type X = Camel<'hello_world_now'>;   // 'helloWorldNow'
```

### Recursive type
```ts
type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;

type Nested = { a: { b: { c: number } } };
type Frozen = DeepReadonly<Nested>;
```

→ TS 5+ 의 recursion limit ↑.

### Tuple manipulation
```ts
type Head<T> = T extends [infer H, ...any[]] ? H : never;
type Tail<T> = T extends [any, ...infer R] ? R : never;
type Length<T extends readonly any[]> = T['length'];

type H = Head<[1, 2, 3]>;     // 1
type T = Tail<[1, 2, 3]>;     // [2, 3]
type L = Length<[1, 2, 3]>;   // 3
```

### Variadic tuple
```ts
function tail<T extends any[]>(arr: [any, ...T]): T {
  const [_, ...rest] = arr;
  return rest;
}

const r = tail([1, 'a', true]);
// → ['a' | true ...] type-safe
```

### Union to intersection
```ts
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

type X = UnionToIntersection<{a: 1} | {b: 2}>;
// → {a: 1} & {b: 2}
```

### Discriminated union → narrow
```ts
type Shape = 
  | { type: 'circle'; radius: number }
  | { type: 'square'; size: number };

function area(s: Shape) {
  if (s.type === 'circle') return Math.PI * s.radius ** 2;
  if (s.type === 'square') return s.size ** 2;
}
```

### Exhaustive check
```ts
function area(s: Shape) {
  switch (s.type) {
    case 'circle': return ...;
    case 'square': return ...;
    default:
      const _: never = s;  // 새 type 추가 시 compile error
      throw new Error('unknown');
  }
}
```

### Branded type
```ts
type UserId = string & { __brand: 'UserId' };
type OrderId = string & { __brand: 'OrderId' };

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

const id = 'abc' as UserId;
getUser(id);             // OK
getUser('abc');          // ❌ string ≠ UserId
```

### Builder pattern (type-safe)
```ts
class QueryBuilder<T = {}> {
  where<K extends string, V>(key: K, value: V): QueryBuilder<T & { [P in K]: V }> {
    return new QueryBuilder<T & { [P in K]: V }>();
  }
  build(): T { ... }
}

const q = new QueryBuilder()
  .where('age', 25)
  .where('name', 'Alice')
  .build();
// type: { age: number; name: string }
```

### Function overload (type)
```ts
function fetchData(url: string): Promise<string>;
function fetchData(url: string, opts: { json: true }): Promise<object>;
function fetchData(url: string, opts?: any): Promise<any> { ... }
```

→ Caller 가 정확 type.

### Object key constraint
```ts
type RequireOne<T, K extends keyof T> = T & Required<Pick<T, K>>;

type User = { id?: string; email?: string };
type ValidUser = RequireOne<User, 'id'>;
// id 필수.
```

### Type predicate
```ts
function isString(x: unknown): x is string {
  return typeof x === 'string';
}

const v: unknown = 'hi';
if (isString(v)) {
  v.toUpperCase();   // OK
}
```

### Assert function
```ts
function assertString(x: unknown): asserts x is string {
  if (typeof x !== 'string') throw new Error();
}

const v: unknown = 'hi';
assertString(v);
v.toUpperCase();  // OK
```

### Awaited (Promise unwrap)
```ts
type X = Awaited<Promise<string>>;       // string
type Y = Awaited<Promise<Promise<number>>>; // number (recursive unwrap)
```

→ Built-in TS 4.5+.

### Parameters / ReturnType
```ts
type Args = Parameters<typeof fn>;       // tuple
type R = ReturnType<typeof fn>;          // 반환
type T = ConstructorParameters<typeof Cls>;
```

### Pick / Omit / Exclude
```ts
type User = { id: string; name: string; password: string };
type PublicUser = Omit<User, 'password'>;
type Required = Pick<User, 'id' | 'name'>;

type Color = 'red' | 'blue' | 'green';
type Primary = Exclude<Color, 'green'>;  // 'red' | 'blue'
```

### Readonly array vs tuple
```ts
const arr1 = [1, 2, 3];                  // number[]
const arr2 = [1, 2, 3] as const;         // readonly [1, 2, 3]
const tuple: [string, number] = ['a', 1];
```

### satisfies (TS 4.9+)
```ts
const config = {
  name: 'Alice',
  age: 25,
} satisfies Record<string, string | number>;

config.age.toFixed(2);  // OK — 정확 type 보존 (age 가 number).
```

→ As 보다 좋음 (type narrowing).

### Generic constraint
```ts
function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const u = { id: '1', name: 'Alice' };
pluck(u, 'id');     // string
pluck(u, 'xxx');    // ❌ 'xxx' 가 키 X
```

### Recursive limit
```ts
// Default ~ 50 deep.
// TS 5+ 가 1000+.

type Reverse<T extends any[]> = T extends [infer F, ...infer R] ? [...Reverse<R>, F] : [];
type R = Reverse<[1, 2, 3, 4, 5]>;  // [5, 4, 3, 2, 1]
```

### Type-fest / ts-toolbelt
```bash
yarn add type-fest
```

```ts
import type { CamelCase, KebabCase, RequireAtLeastOne } from 'type-fest';

type X = CamelCase<'hello_world'>;   // 'helloWorld'
```

→ 일반 utility 가 library.

### Production: tRPC / Drizzle / Hono
```ts
// tRPC 가 type-safe RPC
import { router, procedure } from './trpc';

const appRouter = router({
  user: procedure.input(z.object({ id: z.string() })).query(({ input }) => fetchUser(input.id)),
});

// Client side
const user = await trpc.user.query({ id: '1' });
//      ^ User type — server type 가 client 에 자동 흐름.
```

→ Type-level programming 의 큰 가치.

### 함정: 너무 복잡
```ts
type Crazy<T> = T extends infer U
  ? U extends Record<infer K, any>
    ? K extends string
      ? `${K}_${string & Lowercase<...>}`
      : never
    : never
  : never;
```

→ 이해 어려움. Maintenance 어려움.

→ Simple 가 default. Complex 만 진짜 가치.

### Performance
```
TS compile 가 type-level 폭발 = 느림.
- 매 type 가 recursive O(N) → compile O(N^2).
- 큰 codebase 가 분 단위 compile.

→ 측정 + simplify.
```

```bash
tsc --extendedDiagnostics
# Type instantiations / Memory used.
```

### When 사용?
```
✓ Library author (API surface).
✓ Type-safe DSL (SQL builder).
✓ Form validation (Zod).
✓ Branded type.

✗ Application code (KISS).
✗ 빠른 prototype.
```

## 🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| Library API | Type-level |
| 일반 app | KISS |
| Form / schema | Zod |
| RPC | tRPC |
| Branded ID | Branded type |
| Discriminated union | switch + never |
| Utility | type-fest |

## ❌ 안티패턴
- **너무 복잡**: 가독성 / maintenance ↓.
- **`any` everywhere**: type 의 가치 ↓.
- **`as` 강제**: silent unsoundness.
- **Recursive 무한**: compile fail.
- **Same type 반복**: utility 추출.
- **Type gymnastics 가 기능**: 운영 진짜 가치 검토.

## 🤖 LLM 활용 힌트
- Type-level = compile-time correctness.
- type-fest / ts-toolbelt 가 ready utility.
- tRPC / Drizzle 가 큰 use case.
- `satisfies` 가 modern (4.9+).

## 🔗 관련 문서
- [[TypeScript_Generic_Constraints]]
- [[TypeScript_Branded_Types]]
- [[TypeScript_Template_Literal_Types]]
