@kood/claude-code 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +21 -243
- package/package.json +1 -1
- package/templates/hono/CLAUDE.md +10 -6
- package/templates/hono/docs/deployment/index.md +5 -0
- package/templates/hono/docs/library/hono/index.md +6 -0
- package/templates/hono/docs/library/prisma/index.md +3 -0
- package/templates/npx/CLAUDE.md +8 -2
- package/templates/tanstack-start/CLAUDE.md +103 -255
- package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
- package/templates/tanstack-start/docs/deployment/index.md +57 -286
- package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
- package/templates/tanstack-start/docs/deployment/railway.md +40 -409
- package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
- package/templates/tanstack-start/docs/design/accessibility.md +56 -326
- package/templates/tanstack-start/docs/design/color.md +37 -179
- package/templates/tanstack-start/docs/design/components.md +77 -311
- package/templates/tanstack-start/docs/design/index.md +24 -87
- package/templates/tanstack-start/docs/design/safe-area.md +51 -250
- package/templates/tanstack-start/docs/design/spacing.md +57 -276
- package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
- package/templates/tanstack-start/docs/design/typography.md +40 -284
- package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
- package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
- package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
- package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
- package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
- package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
- package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
- package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
- package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
- package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
- package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
- package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
- package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +12 -99
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
- package/templates/tanstack-start/docs/library/tanstack-query/setup.md +11 -70
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
- package/templates/tanstack-start/docs/library/zod/basic-types.md +33 -145
- package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
- package/templates/tanstack-start/docs/library/zod/index.md +22 -150
- package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
- package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
- package/templates/hono/docs/commands/git.md +0 -145
- package/templates/hono/docs/mcp/context7.md +0 -106
- package/templates/hono/docs/mcp/index.md +0 -176
- package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
- package/templates/hono/docs/mcp/serena.md +0 -269
- package/templates/hono/docs/mcp/sgrep.md +0 -105
- package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
- package/templates/npx/docs/commands/git.md +0 -145
- package/templates/npx/docs/mcp/index.md +0 -60
- package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
- package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
- package/templates/tanstack-start/docs/commands/git.md +0 -145
- package/templates/tanstack-start/docs/mcp/context7.md +0 -204
- package/templates/tanstack-start/docs/mcp/index.md +0 -177
- package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
- package/templates/tanstack-start/docs/mcp/serena.md +0 -269
- package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# TanStack Start - 설치 및 설정
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## 패키지 설치
|
|
3
|
+
## 설치
|
|
6
4
|
|
|
7
5
|
```bash
|
|
8
6
|
yarn add @tanstack/react-start @tanstack/react-router vinxi
|
|
9
7
|
yarn add -D vite @vitejs/plugin-react vite-tsconfig-paths
|
|
10
8
|
```
|
|
11
9
|
|
|
12
|
-
##
|
|
10
|
+
## 설정
|
|
13
11
|
|
|
14
12
|
```typescript
|
|
15
13
|
// vite.config.ts
|
|
@@ -19,19 +17,11 @@ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
|
19
17
|
import viteReact from '@vitejs/plugin-react'
|
|
20
18
|
|
|
21
19
|
export default defineConfig({
|
|
22
|
-
server: {
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
plugins: [
|
|
26
|
-
tsConfigPaths(),
|
|
27
|
-
tanstackStart(),
|
|
28
|
-
viteReact(),
|
|
29
|
-
],
|
|
20
|
+
server: { port: 3000 },
|
|
21
|
+
plugins: [tsConfigPaths(), tanstackStart(), viteReact()],
|
|
30
22
|
})
|
|
31
23
|
```
|
|
32
24
|
|
|
33
|
-
## TypeScript 설정
|
|
34
|
-
|
|
35
25
|
```json
|
|
36
26
|
// tsconfig.json
|
|
37
27
|
{
|
|
@@ -40,35 +30,12 @@ export default defineConfig({
|
|
|
40
30
|
"module": "ESNext",
|
|
41
31
|
"moduleResolution": "bundler",
|
|
42
32
|
"strict": true,
|
|
43
|
-
"esModuleInterop": true,
|
|
44
|
-
"skipLibCheck": true,
|
|
45
33
|
"jsx": "react-jsx",
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
"@/*": ["./src/*"]
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"include": ["src/**/*"]
|
|
34
|
+
"paths": { "@/*": ["./src/*"] }
|
|
35
|
+
}
|
|
52
36
|
}
|
|
53
37
|
```
|
|
54
38
|
|
|
55
|
-
## 프로젝트 구조
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
project/
|
|
59
|
-
├── src/
|
|
60
|
-
│ ├── routes/
|
|
61
|
-
│ │ ├── __root.tsx
|
|
62
|
-
│ │ ├── index.tsx
|
|
63
|
-
│ │ └── about.tsx
|
|
64
|
-
│ ├── lib/
|
|
65
|
-
│ │ └── server-functions.ts
|
|
66
|
-
│ └── start.ts
|
|
67
|
-
├── vite.config.ts
|
|
68
|
-
├── tsconfig.json
|
|
69
|
-
└── package.json
|
|
70
|
-
```
|
|
71
|
-
|
|
72
39
|
## 환경 변수 검증
|
|
73
40
|
|
|
74
41
|
```typescript
|
|
@@ -1,186 +1,74 @@
|
|
|
1
1
|
# Zod - 기본 타입
|
|
2
2
|
|
|
3
|
-
> **상위 문서**: [Zod](./index.md)
|
|
4
|
-
|
|
5
3
|
## 문자열
|
|
6
4
|
|
|
7
5
|
```typescript
|
|
8
6
|
z.string()
|
|
9
|
-
z.string().min(1)
|
|
10
|
-
z.string().
|
|
11
|
-
z.string().
|
|
12
|
-
z.string().regex(/^\d+$/) // 정규식
|
|
13
|
-
z.string().trim() // 공백 제거
|
|
14
|
-
z.string().toLowerCase() // 소문자 변환
|
|
15
|
-
z.string().toUpperCase() // 대문자 변환
|
|
7
|
+
z.string().min(1).max(100).length(5)
|
|
8
|
+
z.string().regex(/^\d+$/)
|
|
9
|
+
z.string().trim().toLowerCase().toUpperCase()
|
|
16
10
|
```
|
|
17
11
|
|
|
18
|
-
### v4
|
|
12
|
+
### v4 최상위 포맷 API
|
|
19
13
|
|
|
20
14
|
```typescript
|
|
21
|
-
|
|
22
|
-
z.
|
|
23
|
-
z.
|
|
24
|
-
|
|
25
|
-
z.
|
|
26
|
-
z.
|
|
27
|
-
z.
|
|
28
|
-
z.
|
|
29
|
-
z.cuid2() // CUID2 형식
|
|
30
|
-
z.ulid() // ULID 형식
|
|
31
|
-
z.emoji() // 이모지 (단일 문자)
|
|
32
|
-
z.ipv4() // IPv4 주소
|
|
33
|
-
z.ipv6() // IPv6 주소
|
|
34
|
-
z.cidrv4() // IPv4 범위
|
|
35
|
-
z.cidrv6() // IPv6 범위
|
|
36
|
-
|
|
37
|
-
// ISO 날짜/시간 형식
|
|
38
|
-
z.iso.date() // ISO 날짜 (2024-01-15)
|
|
39
|
-
z.iso.time() // ISO 시간 (14:30:00)
|
|
40
|
-
z.iso.datetime() // ISO 날짜시간
|
|
41
|
-
z.iso.duration() // ISO 기간 (P1D, PT1H)
|
|
15
|
+
z.email() z.url() z.uuid() z.base64()
|
|
16
|
+
z.nanoid() z.cuid() z.cuid2() z.ulid()
|
|
17
|
+
z.ipv4() z.ipv6() z.cidrv4() z.cidrv6()
|
|
18
|
+
|
|
19
|
+
z.iso.date() // 2024-01-15
|
|
20
|
+
z.iso.time() // 14:30:00
|
|
21
|
+
z.iso.datetime() // ISO 날짜시간
|
|
22
|
+
z.iso.duration() // P1D, PT1H
|
|
42
23
|
```
|
|
43
24
|
|
|
44
|
-
### 템플릿 리터럴
|
|
25
|
+
### 템플릿 리터럴 (v4)
|
|
45
26
|
|
|
46
27
|
```typescript
|
|
47
|
-
const
|
|
48
|
-
// `
|
|
49
|
-
|
|
50
|
-
const cssUnits = z.enum(["px", "em", "rem", "%"])
|
|
51
|
-
const css = z.templateLiteral([z.number(), cssUnits])
|
|
52
|
-
// `${number}px` | `${number}em` | `${number}rem` | `${number}%`
|
|
53
|
-
|
|
54
|
-
const email = z.templateLiteral([
|
|
55
|
-
z.string().min(1),
|
|
56
|
-
"@",
|
|
57
|
-
z.string().max(64),
|
|
58
|
-
])
|
|
59
|
-
// `${string}@${string}` (min/max refinements도 적용됨!)
|
|
28
|
+
const css = z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])])
|
|
29
|
+
// `${number}px` | `${number}em` | `${number}rem`
|
|
60
30
|
```
|
|
61
31
|
|
|
62
32
|
## 숫자
|
|
63
33
|
|
|
64
34
|
```typescript
|
|
65
35
|
z.number()
|
|
66
|
-
z.number().min(0)
|
|
67
|
-
z.number().
|
|
68
|
-
z.number().
|
|
69
|
-
z.number().positive() // 양수만
|
|
70
|
-
z.number().negative() // 음수만
|
|
71
|
-
z.number().nonnegative() // 0 이상
|
|
72
|
-
z.number().nonpositive() // 0 이하
|
|
73
|
-
z.number().finite() // 유한 숫자
|
|
74
|
-
z.number().safe() // 안전한 정수 범위
|
|
36
|
+
z.number().min(0).max(100).int()
|
|
37
|
+
z.number().positive().negative().nonnegative().nonpositive()
|
|
38
|
+
z.number().finite().safe()
|
|
75
39
|
```
|
|
76
40
|
|
|
77
41
|
## 불리언
|
|
78
42
|
|
|
79
43
|
```typescript
|
|
80
44
|
z.boolean()
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### 문자열 불리언 (v4 신규)
|
|
84
|
-
|
|
85
|
-
환경변수 스타일 불리언 변환:
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
const strbool = z.stringbool()
|
|
89
|
-
|
|
90
|
-
strbool.parse("true") // => true
|
|
91
|
-
strbool.parse("1") // => true
|
|
92
|
-
strbool.parse("yes") // => true
|
|
93
|
-
strbool.parse("on") // => true
|
|
94
|
-
strbool.parse("enabled") // => true
|
|
95
|
-
|
|
96
|
-
strbool.parse("false") // => false
|
|
97
|
-
strbool.parse("0") // => false
|
|
98
|
-
strbool.parse("no") // => false
|
|
99
|
-
strbool.parse("off") // => false
|
|
100
|
-
strbool.parse("disabled") // => false
|
|
101
|
-
|
|
102
|
-
// 커스텀 truthy/falsy 값
|
|
103
|
-
z.stringbool({
|
|
104
|
-
truthy: ["yes", "true"],
|
|
105
|
-
falsy: ["no", "false"]
|
|
106
|
-
})
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## 날짜
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
z.date()
|
|
113
|
-
z.date().min(new Date('2020-01-01'))
|
|
114
|
-
z.date().max(new Date('2030-12-31'))
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## BigInt
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
z.bigint()
|
|
121
|
-
z.bigint().positive()
|
|
122
|
-
z.bigint().negative()
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## 리터럴
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
z.literal('hello') // 정확히 'hello'만
|
|
129
|
-
z.literal(42) // 정확히 42만
|
|
130
|
-
z.literal(true) // 정확히 true만
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Null, Undefined, Void
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
z.null() // null만 허용
|
|
137
|
-
z.undefined() // undefined만 허용
|
|
138
|
-
z.void() // undefined 허용 (반환 타입용)
|
|
139
|
-
```
|
|
140
45
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
z.any() // 모든 타입 허용
|
|
145
|
-
z.unknown() // unknown 타입
|
|
146
|
-
z.never() // 값 없음 (유니온에서 사용)
|
|
46
|
+
// v4 문자열 불리언 (환경변수용)
|
|
47
|
+
z.stringbool() // "true"/"yes"/"1" → true, "false"/"no"/"0" → false
|
|
147
48
|
```
|
|
148
49
|
|
|
149
|
-
|
|
150
|
-
> ```typescript
|
|
151
|
-
> const mySchema = z.object({ a: z.any(), b: z.unknown() })
|
|
152
|
-
> // v3: { a?: any; b?: unknown }
|
|
153
|
-
> // v4: { a: any; b: unknown }
|
|
154
|
-
> ```
|
|
155
|
-
|
|
156
|
-
## Optional & Nullable
|
|
50
|
+
## 날짜/BigInt/리터럴
|
|
157
51
|
|
|
158
52
|
```typescript
|
|
159
|
-
z.
|
|
160
|
-
z.
|
|
161
|
-
z.
|
|
53
|
+
z.date().min(new Date('2020-01-01')).max(new Date('2030-12-31'))
|
|
54
|
+
z.bigint().positive().negative()
|
|
55
|
+
z.literal('hello') z.literal(42) z.literal(true)
|
|
162
56
|
```
|
|
163
57
|
|
|
164
|
-
##
|
|
58
|
+
## 특수 타입
|
|
165
59
|
|
|
166
60
|
```typescript
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
role: z.enum(['admin', 'user']).default('user'),
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
Schema.parse({}) // { name: 'Anonymous', role: 'user' }
|
|
61
|
+
z.null() z.undefined() z.void()
|
|
62
|
+
z.any() z.unknown() z.never()
|
|
173
63
|
```
|
|
174
64
|
|
|
175
|
-
##
|
|
65
|
+
## 수정자
|
|
176
66
|
|
|
177
67
|
```typescript
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
type NumberType = z.infer<typeof numberSchema> // number | undefined
|
|
68
|
+
z.string().optional() // string | undefined
|
|
69
|
+
z.string().nullable() // string | null
|
|
70
|
+
z.string().nullish() // string | null | undefined
|
|
71
|
+
z.string().default('Anonymous')
|
|
183
72
|
|
|
184
|
-
|
|
185
|
-
type DateType = z.infer<typeof dateSchema> // Date | null
|
|
73
|
+
type T = z.infer<typeof schema>
|
|
186
74
|
```
|
|
@@ -1,204 +1,80 @@
|
|
|
1
1
|
# Zod - 복합 타입
|
|
2
2
|
|
|
3
|
-
> **상위 문서**: [Zod](./index.md)
|
|
4
|
-
|
|
5
3
|
## 객체
|
|
6
4
|
|
|
7
5
|
```typescript
|
|
8
6
|
const UserSchema = z.object({
|
|
9
7
|
name: z.string(),
|
|
10
|
-
email: z.email(),
|
|
11
|
-
age: z.number().optional(),
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
// 검증
|
|
15
|
-
const result = UserSchema.parse({
|
|
16
|
-
name: 'john',
|
|
17
|
-
email: 'john@example.com',
|
|
18
|
-
age: 25,
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
// 안전한 검증
|
|
22
|
-
const safeResult = UserSchema.safeParse(data)
|
|
23
|
-
if (safeResult.success) {
|
|
24
|
-
console.log(safeResult.data)
|
|
25
|
-
} else {
|
|
26
|
-
console.log(safeResult.error)
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### 객체 메서드
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
// 부분 객체 (모든 필드가 optional)
|
|
34
|
-
const PartialUser = UserSchema.partial()
|
|
35
|
-
|
|
36
|
-
// 필수 객체 (모든 필드가 required)
|
|
37
|
-
const RequiredUser = UserSchema.required()
|
|
38
|
-
|
|
39
|
-
// 필드 선택
|
|
40
|
-
const UserName = UserSchema.pick({ name: true })
|
|
41
|
-
|
|
42
|
-
// 필드 제외
|
|
43
|
-
const UserWithoutEmail = UserSchema.omit({ email: true })
|
|
44
|
-
|
|
45
|
-
// 확장
|
|
46
|
-
const ExtendedUser = UserSchema.extend({
|
|
47
|
-
role: z.enum(['admin', 'user']),
|
|
8
|
+
email: z.email(),
|
|
9
|
+
age: z.number().optional(),
|
|
48
10
|
})
|
|
49
11
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
12
|
+
// 메서드
|
|
13
|
+
UserSchema.partial() // 모든 필드 optional
|
|
14
|
+
UserSchema.required() // 모든 필드 required
|
|
15
|
+
UserSchema.pick({ name: true }) // 특정 필드만
|
|
16
|
+
UserSchema.omit({ email: true }) // 특정 필드 제외
|
|
17
|
+
UserSchema.extend({ role: z.enum(['admin', 'user']) })
|
|
18
|
+
UserSchema.merge(AnotherSchema)
|
|
55
19
|
|
|
56
|
-
|
|
57
|
-
// v3
|
|
58
|
-
z.object({ name: z.string() }).strict() // 추가 키 에러
|
|
59
|
-
z.object({ name: z.string() }).passthrough() // 추가 키 통과
|
|
60
|
-
|
|
61
|
-
// v4 - 새로운 API
|
|
20
|
+
// v4 Strict/Loose
|
|
62
21
|
z.strictObject({ name: z.string() }) // 추가 키 에러
|
|
63
22
|
z.looseObject({ name: z.string() }) // 추가 키 통과
|
|
64
23
|
```
|
|
65
24
|
|
|
66
|
-
##
|
|
25
|
+
## 배열/튜플
|
|
67
26
|
|
|
68
27
|
```typescript
|
|
69
28
|
z.array(z.string())
|
|
70
|
-
z.array(z.number()).min(1)
|
|
71
|
-
z.array(z.number()).max(10) // 최대 10개
|
|
72
|
-
z.array(z.number()).length(5) // 정확히 5개
|
|
73
|
-
z.array(z.number()).nonempty() // 비어있지 않음
|
|
74
|
-
```
|
|
29
|
+
z.array(z.number()).min(1).max(10).length(5).nonempty()
|
|
75
30
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
const tuple = z.tuple([
|
|
80
|
-
z.string(), // 첫 번째 요소
|
|
81
|
-
z.number(), // 두 번째 요소
|
|
82
|
-
])
|
|
83
|
-
|
|
84
|
-
type Tuple = z.infer<typeof tuple>
|
|
85
|
-
// [string, number]
|
|
31
|
+
z.tuple([z.string(), z.number()]) // [string, number]
|
|
86
32
|
```
|
|
87
33
|
|
|
88
34
|
## 유니온
|
|
89
35
|
|
|
90
36
|
```typescript
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const StringOrNumber = z.string().or(z.number())
|
|
94
|
-
|
|
95
|
-
type StringOrNumber = z.infer<typeof StringOrNumber>
|
|
96
|
-
// string | number
|
|
97
|
-
```
|
|
37
|
+
z.union([z.string(), z.number()])
|
|
38
|
+
z.string().or(z.number())
|
|
98
39
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
const Shape = z.discriminatedUnion('type', [
|
|
40
|
+
// Discriminated Union
|
|
41
|
+
z.discriminatedUnion('type', [
|
|
103
42
|
z.object({ type: z.literal('circle'), radius: z.number() }),
|
|
104
43
|
z.object({ type: z.literal('rectangle'), width: z.number(), height: z.number() }),
|
|
105
44
|
])
|
|
106
|
-
|
|
107
|
-
type Shape = z.infer<typeof Shape>
|
|
108
|
-
// { type: 'circle'; radius: number } | { type: 'rectangle'; width: number; height: number }
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### v4 향상된 Discriminated Union
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// 유니온 및 파이프 discriminator 지원
|
|
115
|
-
const MyResult = z.discriminatedUnion("status", [
|
|
116
|
-
// 단순 리터럴
|
|
117
|
-
z.object({ status: z.literal("aaa"), data: z.string() }),
|
|
118
|
-
// 유니온 discriminator
|
|
119
|
-
z.object({ status: z.union([z.literal("bbb"), z.literal("ccc")]) }),
|
|
120
|
-
// 파이프 discriminator
|
|
121
|
-
z.object({ status: z.literal("fail").transform(val => val.toUpperCase()) }),
|
|
122
|
-
])
|
|
123
|
-
|
|
124
|
-
// 중첩 discriminated union
|
|
125
|
-
const BaseError = z.object({ status: z.literal("failed"), message: z.string() })
|
|
126
|
-
|
|
127
|
-
const MyResult2 = z.discriminatedUnion("status", [
|
|
128
|
-
z.object({ status: z.literal("success"), data: z.string() }),
|
|
129
|
-
z.discriminatedUnion("code", [
|
|
130
|
-
BaseError.extend({ code: z.literal(400) }),
|
|
131
|
-
BaseError.extend({ code: z.literal(401) }),
|
|
132
|
-
BaseError.extend({ code: z.literal(500) })
|
|
133
|
-
])
|
|
134
|
-
])
|
|
135
45
|
```
|
|
136
46
|
|
|
137
47
|
## Enum
|
|
138
48
|
|
|
139
49
|
```typescript
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
FishEnum.parse('Salmon') // => "Salmon"
|
|
144
|
-
FishEnum.parse('Swordfish') // => ❌ throws
|
|
145
|
-
|
|
146
|
-
type Fish = z.infer<typeof FishEnum>
|
|
147
|
-
// 'Salmon' | 'Tuna' | 'Trout'
|
|
50
|
+
const Status = z.enum(['pending', 'done', 'cancelled'])
|
|
51
|
+
type Status = z.infer<typeof Status> // 'pending' | 'done' | 'cancelled'
|
|
148
52
|
|
|
149
53
|
// Native enum
|
|
150
|
-
enum Fruits {
|
|
151
|
-
|
|
152
|
-
Banana,
|
|
153
|
-
}
|
|
154
|
-
const FruitSchema = z.nativeEnum(Fruits)
|
|
54
|
+
enum Fruits { Apple, Banana }
|
|
55
|
+
z.nativeEnum(Fruits)
|
|
155
56
|
```
|
|
156
57
|
|
|
157
|
-
## Record
|
|
58
|
+
## Record/Map/Set
|
|
158
59
|
|
|
159
60
|
```typescript
|
|
160
|
-
|
|
161
|
-
name: z.string(),
|
|
162
|
-
}))
|
|
163
|
-
|
|
164
|
-
type UserStore = z.infer<typeof UserStore>
|
|
61
|
+
z.record(z.string(), z.object({ name: z.string() }))
|
|
165
62
|
// { [key: string]: { name: string } }
|
|
166
63
|
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
userStore['77d2586b-9e8e-4ecf-8b21-ea7e0530eadd'] = {
|
|
170
|
-
name: 'Carlotta',
|
|
171
|
-
} // passes
|
|
172
|
-
|
|
173
|
-
userStore['77d2586b-9e8e-4ecf-8b21-ea7e0530eadd'] = {
|
|
174
|
-
whatever: 'Ice cream sundae',
|
|
175
|
-
} // TypeError
|
|
64
|
+
z.map(z.string(), z.number()) // Map<string, number>
|
|
65
|
+
z.set(z.number()) // Set<number>
|
|
176
66
|
```
|
|
177
67
|
|
|
178
|
-
##
|
|
68
|
+
## 재귀 스키마
|
|
179
69
|
|
|
180
70
|
```typescript
|
|
181
|
-
|
|
182
|
-
const stringNumberMap = z.map(z.string(), z.number())
|
|
183
|
-
type StringNumberMap = z.infer<typeof stringNumberMap>
|
|
184
|
-
// Map<string, number>
|
|
185
|
-
|
|
186
|
-
// Set
|
|
187
|
-
const numberSet = z.set(z.number())
|
|
188
|
-
type NumberSet = z.infer<typeof numberSet>
|
|
189
|
-
// Set<number>
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## 재귀 스키마 (JSON)
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()])
|
|
196
|
-
type Literal = z.infer<typeof literalSchema>
|
|
197
|
-
type Json = Literal | { [key: string]: Json } | Json[]
|
|
71
|
+
type Json = string | number | boolean | null | { [key: string]: Json } | Json[]
|
|
198
72
|
|
|
199
73
|
const jsonSchema: z.ZodType<Json> = z.lazy(() =>
|
|
200
|
-
z.union([
|
|
74
|
+
z.union([
|
|
75
|
+
z.string(), z.number(), z.boolean(), z.null(),
|
|
76
|
+
z.array(jsonSchema),
|
|
77
|
+
z.record(jsonSchema)
|
|
78
|
+
])
|
|
201
79
|
)
|
|
202
|
-
|
|
203
|
-
jsonSchema.parse({ foo: [1, 2, { bar: 'baz' }] })
|
|
204
80
|
```
|