|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +`@kakasoo/deep-strict-types`는 중첩된 배열과 객체 내부의 키를 타입 안전하게 Pick, Omit, 탐색할 수 있는 TypeScript 유틸리티 타입 라이브러리입니다. |
| 8 | + |
| 9 | +npm 패키지로 배포되며, 순수 타입 수준(type-level)의 연산과 런타임 함수를 모두 제공합니다. |
| 10 | + |
| 11 | +## Build & Test Commands |
| 12 | + |
| 13 | +```bash |
| 14 | +# 빌드 |
| 15 | +npm run build # rimraf bin && tsc (src만 빌드) |
| 16 | +npm run build:test # rimraf bin && tsc -p test/tsconfig.json (테스트 포함 빌드) |
| 17 | + |
| 18 | +# 테스트 (빌드 후 실행) |
| 19 | +npm run build:test && npm run test |
| 20 | + |
| 21 | +# 특정 테스트만 실행 |
| 22 | +npm run build:test && npm run test -- --include DeepStrictPick |
| 23 | + |
| 24 | +# 포매팅 |
| 25 | +npm run prettier |
| 26 | + |
| 27 | +# typia 패치 (최초 세팅) |
| 28 | +npm run prepare # ts-patch install && typia patch |
| 29 | +``` |
| 30 | + |
| 31 | +### 테스트 실행 방식 |
| 32 | + |
| 33 | +테스트는 `@nestia/e2e`의 `DynamicExecutor`를 사용합니다. `test/features/` 디렉토리에서 `test_` prefix가 붙은 export 함수를 자동으로 수집하여 실행합니다. **Jest/Mocha 등의 테스트 프레임워크를 사용하지 않습니다.** |
| 34 | + |
| 35 | +## Architecture |
| 36 | + |
| 37 | +``` |
| 38 | +src/ |
| 39 | + types/ # 순수 타입 정의 (type-level 연산) |
| 40 | + functions/ # 런타임 함수 (타입과 연동) |
| 41 | + index.ts # re-export |
| 42 | +
|
| 43 | +test/ |
| 44 | + features/ # 테스트 파일 (test_types_*, test_functions_*) |
| 45 | + helpers/ # 테스트 유틸리티 |
| 46 | + index.ts # DynamicExecutor 기반 테스트 러너 |
| 47 | +``` |
| 48 | + |
| 49 | +### 핵심 타입 |
| 50 | + |
| 51 | +| 타입 | 역할 | |
| 52 | +|------|------| |
| 53 | +| `DeepStrictObjectKeys<T>` | 중첩 객체의 모든 키를 dot notation으로 추출 (`a.b`, `c[*].d`) | |
| 54 | +| `DeepStrictOmit<T, K>` | 중첩 키를 기준으로 deep omit | |
| 55 | +| `DeepStrictPick<T, K>` | 중첩 키를 기준으로 deep pick | |
| 56 | +| `DeepStrictMerge<T, U>` | 두 객체를 deep merge | |
| 57 | +| `DeepDateToString<T>` | Date 타입을 string으로 재귀 변환 | |
| 58 | +| `DeepStrictUnbrand<T>` | 브랜드 타입 제거 | |
| 59 | +| `Equal<A, B>` | 두 타입의 동등성 검사 (테스트용) | |
| 60 | + |
| 61 | +### 핵심 함수 |
| 62 | + |
| 63 | +| 함수 | 역할 | |
| 64 | +|------|------| |
| 65 | +| `deepStrictAssert<T>(input)<K>(key)` | 런타임에서 특정 키만 추출 (타입 안전) | |
| 66 | +| `deepStrictObjectKeys<T>(input)` | 런타임에서 모든 중첩 키 문자열 배열 반환 | |
| 67 | + |
| 68 | +## Coding Conventions |
| 69 | + |
| 70 | +### 타입 작성 규칙 |
| 71 | + |
| 72 | +1. **PascalCase** 사용 (예: `DeepStrictPick`, `RemoveAfterDot`) |
| 73 | +2. **JSDoc 필수** - 모든 export 타입에 `@title`, 설명, 예시 코드 포함 |
| 74 | +3. **JSDoc은 영어로 작성** - npm 패키지의 국제적 사용을 고려 |
| 75 | +4. **타입 간 의존성은 import type 사용** |
| 76 | +5. **namespace를 활용한 내부 타입 구조화** - 외부 노출 타입과 내부 추론 타입 분리 (예: `DeepStrictOmit.Infer`) |
| 77 | +6. **`[*]` 표기법** - 배열 내부 접근 시 사용 (예: `c[*].d`) |
| 78 | +7. **Date 타입 특별 처리** - object이지만 재귀 탐색하지 않음 |
| 79 | + |
| 80 | +### 파일 구조 규칙 |
| 81 | + |
| 82 | +- 하나의 타입/함수 = 하나의 파일 (파일명 = 타입명) |
| 83 | +- `index.ts`에서 모든 public 타입/함수를 re-export |
| 84 | +- 새 타입/함수 추가 시 반드시 `src/types/index.ts` 또는 `src/functions/index.ts`에 export 추가 |
| 85 | + |
| 86 | +### 테스트 규칙 |
| 87 | + |
| 88 | +- **함수명**: `test_types_<type_name>_<scenario>` 또는 `test_functions_<func_name>_<scenario>` |
| 89 | +- **반드시 export** (DynamicExecutor가 수집하기 위해) |
| 90 | +- **파라미터 없음** (M24과 달리 TestOption을 받지 않음) |
| 91 | +- **검증 방법**: `Equal<Question, Answer>` 타입으로 타입 레벨 검증 + `typia.random<Answer>()`로 런타임 확인 |
| 92 | +- **파일명**: 테스트 대상 타입/함수와 동일한 이름 |
| 93 | +- **하나의 파일에 여러 테스트 함수** 가능 (시나리오별 분리) |
| 94 | + |
| 95 | +### 테스트 패턴 |
| 96 | + |
| 97 | +```typescript |
| 98 | +import { ok } from 'assert'; |
| 99 | +import typia from 'typia'; |
| 100 | +import { DeepStrictPick, Equal } from '../../src'; |
| 101 | + |
| 102 | +export function test_types_deep_strict_pick_nested() { |
| 103 | + type Question = DeepStrictPick<{ a: { b: 1; c: 2 } }, "a.b">; |
| 104 | + type Answer = Equal<Question, { a: { b: 1 } }>; |
| 105 | + ok(typia.random<Answer>()); |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +## Git Conventions |
| 110 | + |
| 111 | +### 브랜치 규칙 |
| 112 | + |
| 113 | +- 기본 브랜치: `main` |
| 114 | +- 기능 브랜치: `feature/<name>`, `fix/<name>`, `docs/<name>` |
| 115 | + |
| 116 | +### 커밋 메시지 |
| 117 | + |
| 118 | +영어로 작성하며 다음 prefix를 사용합니다: |
| 119 | + |
| 120 | +| Prefix | 용도 | |
| 121 | +|--------|------| |
| 122 | +| `feat` | 새로운 타입/함수 추가 | |
| 123 | +| `fix` | 타입 버그 수정 | |
| 124 | +| `test` | 테스트 추가/수정 | |
| 125 | +| `docs` | 문서/주석 변경 | |
| 126 | +| `refactor` | 리팩토링 | |
| 127 | +| `style` | 코드 포맷팅 | |
| 128 | +| `chore` | 빌드, 설정 변경 | |
| 129 | + |
| 130 | +형식: `<prefix>: <영어 설명>` |
| 131 | + |
| 132 | +## CI/CD |
| 133 | + |
| 134 | +- main 브랜치에 push + `src/` 또는 `package.json` 변경 시 자동으로: |
| 135 | + 1. npm install + build |
| 136 | + 2. `npm version patch` (자동 버전 증가) |
| 137 | + 3. npm publish |
| 138 | + 4. git push --follow-tags |
| 139 | + |
| 140 | +## Dependencies |
| 141 | + |
| 142 | +- **런타임**: `@kakasoo/proto-typescript` (프로토타입 유틸리티) |
| 143 | +- **개발**: `typia` (런타임 검증/랜덤 생성), `@nestia/e2e` (테스트 러너), `ts-patch` (typia 트랜스폼) |
| 144 | + |
| 145 | +## 비판적 피드백 원칙 |
| 146 | + |
| 147 | +사용자의 요청이 다음에 해당하면 **즉시 지적하고 대안을 제시**합니다: |
| 148 | + |
| 149 | +- 타입 레벨 연산에서 `any`, `as` 캐스팅 사용 시도 |
| 150 | +- Utility Type (`Partial`, `Pick`, `Omit` 등) 직접 사용 (이 라이브러리가 deep 버전을 제공) |
| 151 | +- Date를 일반 object처럼 재귀 탐색하려는 시도 |
| 152 | +- 테스트 없이 새 타입 추가 시도 |
| 153 | +- `index.ts` re-export 누락 |
0 commit comments