Skip to content

Commit 9d806ab

Browse files
committed
fix: Handle optional fields
1 parent e00648a commit 9d806ab

4 files changed

Lines changed: 66 additions & 23 deletions

File tree

templates/api.ejs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -99,29 +99,39 @@ type Flatten<T> = T extends any[] ? T[number] : T
9999

100100
type IsArray<T> = T extends Array<any> ? true : false
101101
type IsAny<T> = 0 extends (1 & T) ? true : false;
102-
103-
type DotNotationKeys<T, P extends string = ''> = IsAny<T> extends true ? P : T extends object
102+
type ExcludeUndefined<T> = T extends undefined ? never : T
103+
104+
type DotNotationKeys<T, P extends string = ''> = IsAny<T> extends true
105+
? P
106+
: T extends object
104107
? IsArray<T> extends true
105108
? P extends ''
106109
? never
107110
: P
108111
: {
109-
[K in keyof T]: K extends string
112+
[K in keyof T]-?: ExcludeUndefined<K> extends string // Exclude undefined keys
110113
? P extends ''
111-
? K | DotNotationKeys<T[K], K>
112-
: `${P}.${K}` | DotNotationKeys<T[K], `${P}.${K}`>
114+
?
115+
| ExcludeUndefined<K>
116+
| DotNotationKeys<ExcludeUndefined<T[K]>, ExcludeUndefined<K>>
117+
:
118+
| `${P}.${ExcludeUndefined<K>}`
119+
| DotNotationKeys<
120+
ExcludeUndefined<T[K]>,
121+
`${P}.${ExcludeUndefined<K>}`
122+
>
113123
: never
114124
}[keyof T]
115125
: P
116-
117-
type FetchKeysArray<ResponseModel> = ResponseModel extends (infer DataModel)[]
118-
? DotNotationKeys<DataModel>[]
119-
: ResponseModel extends {
120-
results?: Array<infer DataModel>
121-
count?: number
122-
}
123-
? DotNotationKeys<DataModel>[]
124-
: DotNotationKeys<ResponseModel>[]
126+
127+
type FetchKeysArray<ResponseModel> = ResponseModel extends (infer DataModel)[]
128+
? DotNotationKeys<DataModel>[]
129+
: ResponseModel extends {
130+
results?: Array<infer DataModel>
131+
count?: number
132+
}
133+
? DotNotationKeys<DataModel>[]
134+
: DotNotationKeys<ResponseModel>[]
125135

126136
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
127137
k: infer I
@@ -151,7 +161,7 @@ type Flatten<T> = T extends any[] ? T[number] : T
151161

152162
type Modify<T, R> = Omit<T, keyof R> & R;
153163

154-
type PickKeysFromArray<ResponseModel, Keys extends string[]> = Keys extends never[]
164+
type PickKeysFromArray<ResponseModel, Keys extends string[]> = never[] extends Keys
155165
? ResponseModel
156166
: ResponseModel extends (infer DataModel)[]
157167
? Merge<ObjectWithKeysFromArray<DataModel, Keys>>[]

test/__generated-api.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,7 @@ function getStringifiedQueryKeys(keys: FetchKeysObject<unknown>) {
17301730

17311731
type IsArray<T> = T extends Array<any> ? true : false
17321732
type IsAny<T> = 0 extends 1 & T ? true : false
1733+
type ExcludeUndefined<T> = T extends undefined ? never : T
17331734

17341735
type DotNotationKeys<T, P extends string = ''> = IsAny<T> extends true
17351736
? P
@@ -1739,10 +1740,17 @@ type DotNotationKeys<T, P extends string = ''> = IsAny<T> extends true
17391740
? never
17401741
: P
17411742
: {
1742-
[K in keyof T]: K extends string
1743+
[K in keyof T]-?: ExcludeUndefined<K> extends string // Exclude undefined keys
17431744
? P extends ''
1744-
? K | DotNotationKeys<T[K], K>
1745-
: `${P}.${K}` | DotNotationKeys<T[K], `${P}.${K}`>
1745+
?
1746+
| ExcludeUndefined<K>
1747+
| DotNotationKeys<ExcludeUndefined<T[K]>, ExcludeUndefined<K>>
1748+
:
1749+
| `${P}.${ExcludeUndefined<K>}`
1750+
| DotNotationKeys<
1751+
ExcludeUndefined<T[K]>,
1752+
`${P}.${ExcludeUndefined<K>}`
1753+
>
17461754
: never
17471755
}[keyof T]
17481756
: P
@@ -1787,7 +1795,7 @@ type Modify<T, R> = Omit<T, keyof R> & R
17871795
type PickKeysFromArray<
17881796
ResponseModel,
17891797
Keys extends string[]
1790-
> = Keys extends never[]
1798+
> = never[] extends Keys
17911799
? ResponseModel
17921800
: ResponseModel extends (infer DataModel)[]
17931801
? Merge<ObjectWithKeysFromArray<DataModel, Keys>>[]

test/__snapshots__/index.test.ts.snap

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,7 @@ function getStringifiedQueryKeys(keys: FetchKeysObject<unknown>) {
17331733
17341734
type IsArray<T> = T extends Array<any> ? true : false
17351735
type IsAny<T> = 0 extends 1 & T ? true : false
1736+
type ExcludeUndefined<T> = T extends undefined ? never : T
17361737
17371738
type DotNotationKeys<T, P extends string = ''> = IsAny<T> extends true
17381739
? P
@@ -1742,10 +1743,17 @@ type DotNotationKeys<T, P extends string = ''> = IsAny<T> extends true
17421743
? never
17431744
: P
17441745
: {
1745-
[K in keyof T]: K extends string
1746+
[K in keyof T]-?: ExcludeUndefined<K> extends string // Exclude undefined keys
17461747
? P extends ''
1747-
? K | DotNotationKeys<T[K], K>
1748-
: \`\${P}.\${K}\` | DotNotationKeys<T[K], \`\${P}.\${K}\`>
1748+
?
1749+
| ExcludeUndefined<K>
1750+
| DotNotationKeys<ExcludeUndefined<T[K]>, ExcludeUndefined<K>>
1751+
:
1752+
| \`\${P}.\${ExcludeUndefined<K>}\`
1753+
| DotNotationKeys<
1754+
ExcludeUndefined<T[K]>,
1755+
\`\${P}.\${ExcludeUndefined<K>}\`
1756+
>
17491757
: never
17501758
}[keyof T]
17511759
: P
@@ -1790,7 +1798,7 @@ type Modify<T, R> = Omit<T, keyof R> & R
17901798
type PickKeysFromArray<
17911799
ResponseModel,
17921800
Keys extends string[]
1793-
> = Keys extends never[]
1801+
> = never[] extends Keys
17941802
? ResponseModel
17951803
: ResponseModel extends (infer DataModel)[]
17961804
? Merge<ObjectWithKeysFromArray<DataModel, Keys>>[]

test/index.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ describe('postprocessQuery', () => {
9797
})
9898

9999
describe('PickKeys', () => {
100+
it("doesn't change the response model by default", () => {
101+
type ResponseModel = { key1?: string }
102+
type WithPickKeys = PickKeys<ResponseModel, FetchKeys<ResponseModel>>
103+
expectTypeOf<WithPickKeys>().toEqualTypeOf<ResponseModel>()
104+
})
100105
describe('based on FetchKeys object', () => {
101106
it('picks keys from object', () => {
102107
type MyObject = PickKeys<
@@ -133,6 +138,12 @@ describe('PickKeys', () => {
133138
{ key1: string }[]
134139
>()
135140
})
141+
it('handles optional fields', () => {
142+
type ResponseModel = { key1?: string }
143+
expectTypeOf<PickKeys<ResponseModel, { key1: true }>>().toEqualTypeOf<{
144+
key1: string | undefined
145+
}>()
146+
})
136147
})
137148
describe('based on FetchKeys array', () => {
138149
it('picks keys from object', () => {
@@ -172,6 +183,12 @@ describe('PickKeys', () => {
172183
{ key1: string }[]
173184
>()
174185
})
186+
it('handles optional fields', () => {
187+
type ResponseModel = { key1?: string }
188+
expectTypeOf<PickKeys<ResponseModel, ['key1']>>().toEqualTypeOf<{
189+
key1: string | undefined
190+
}>()
191+
})
175192
})
176193
})
177194

0 commit comments

Comments
 (0)