Skip to content

Commit 1941b47

Browse files
fix: nested root keys typescript
1 parent e66cde5 commit 1941b47

File tree

2 files changed

+52
-35
lines changed

2 files changed

+52
-35
lines changed

example/src/App.tsx

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import {
1010
Title,
1111
} from 'react-native-paper';
1212

13+
type AddressCompany = {
14+
name: string;
15+
};
1316
type AddressType = {
1417
street: string;
1518
houseNumber: string;
19+
company?: AddressCompany | null;
1620
};
1721
type FormType = {
1822
email: string;
@@ -26,7 +30,7 @@ type FormType = {
2630
telephone: string;
2731
revenue: number;
2832
};
29-
address?: AddressType;
33+
address?: AddressType | null;
3034
};
3135
export default function App() {
3236
const [
@@ -57,6 +61,8 @@ export default function App() {
5761
}
5862
);
5963

64+
const dd = fh.raw('address.company');
65+
console.log({ dd });
6066
console.log({ values, errors });
6167
return (
6268
<View
@@ -186,7 +192,7 @@ export default function App() {
186192
</HelperText>
187193

188194
<AddressEdit {...fh.raw('address')} />
189-
195+
<AddressCompanyEdit {...fh.raw('address.company')} />
190196
<Button mode="contained" onPress={submit} style={{ marginTop: 24 }}>
191197
Save
192198
</Button>
@@ -200,11 +206,11 @@ function AddressEdit({
200206
onChange,
201207
...rest
202208
}: {
203-
value: AddressType | undefined;
204-
onChange: (v: AddressType | undefined) => void;
209+
value: AddressType | null | undefined;
210+
onChange: (v: AddressType | null | undefined) => void;
205211
}) {
206212
const [{ formProps }, fh] = useFormState<AddressType>(
207-
value || { street: '', houseNumber: '' },
213+
value || { street: '', houseNumber: '', company: { name: '' } },
208214
{
209215
onChange,
210216
}
@@ -228,6 +234,30 @@ function AddressEdit({
228234
);
229235
}
230236

237+
function AddressCompanyEdit({
238+
value,
239+
onChange,
240+
...rest
241+
}: {
242+
value: AddressCompany | undefined | null;
243+
onChange: (v: AddressCompany | undefined | null) => void;
244+
}) {
245+
const [{ formProps }, fh] = useFormState<AddressCompany>(
246+
value || { name: '' },
247+
{
248+
onChange,
249+
}
250+
);
251+
return (
252+
<Surface {...rest}>
253+
<Title>Nested form</Title>
254+
<Form {...formProps}>
255+
<TextInput mode="outlined" label="Street" {...fh.text('name')} />
256+
</Form>
257+
</Surface>
258+
);
259+
}
260+
231261
function looksLikeTelephone(str: string): boolean {
232262
if (str.length !== 10) {
233263
return false;

src/types.ts

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,52 @@
11
// https://dev.to/tipsy_dev/advanced-typescript-reinventing-lodash-get-4fhe
2-
type GetIndexedField<T, K> = K extends keyof T
3-
? T[K]
2+
type GetIndexedField<T, K> = K extends keyof NonNullable<T>
3+
? NonNullable<T>[K]
44
: K extends `${number}`
5-
? '0' extends keyof T
5+
? '0' extends keyof NonNullable<T>
66
? undefined
7-
: number extends keyof T
8-
? T[number]
7+
: number extends keyof NonNullable<T>
8+
? NonNullable<T>[number]
99
: undefined
1010
: undefined;
1111
type FieldWithPossiblyUndefined<T, Key> =
12-
| GetFieldType<Exclude<T, undefined>, Key>
13-
| Extract<T, undefined>;
12+
| GetFieldType<Exclude<NonNullable<T>, undefined>, Key>
13+
| Extract<NonNullable<T>, undefined>;
1414
type IndexedFieldWithPossiblyUndefined<T, Key> =
15-
| GetIndexedField<Exclude<T, undefined>, Key>
16-
| Extract<T, undefined>;
15+
| GetIndexedField<Exclude<NonNullable<T>, undefined>, Key>
16+
| Extract<NonNullable<T>, undefined>;
1717
export type GetFieldType<T, P> = P extends `${infer Left}.${infer Right}`
18-
? Left extends keyof T
19-
? FieldWithPossiblyUndefined<T[Left], Right>
18+
? Left extends keyof NonNullable<T>
19+
? FieldWithPossiblyUndefined<NonNullable<T>[Left], Right>
2020
: Left extends `${infer FieldKey}[${infer IndexKey}]`
21-
? FieldKey extends keyof T
21+
? FieldKey extends keyof NonNullable<T>
2222
? FieldWithPossiblyUndefined<
23-
IndexedFieldWithPossiblyUndefined<T[FieldKey], IndexKey>,
23+
IndexedFieldWithPossiblyUndefined<NonNullable<T>[FieldKey], IndexKey>,
2424
Right
2525
>
2626
: undefined
2727
: undefined
2828
: P extends keyof T
29-
? T[P]
29+
? NonNullable<T>[P]
3030
: P extends `${infer FieldKey}[${infer IndexKey}]`
31-
? FieldKey extends keyof T
32-
? IndexedFieldWithPossiblyUndefined<T[FieldKey], IndexKey>
31+
? FieldKey extends keyof NonNullable<T>
32+
? IndexedFieldWithPossiblyUndefined<NonNullable<T>[FieldKey], IndexKey>
3333
: undefined
3434
: undefined;
3535

3636
// https://stackoverflow.com/questions/58434389/typescript-deep-keyof-of-a-nested-object
3737

3838
export type DotPrefix<T extends string> = T extends '' ? '' : `.${T}`;
39-
// export type DotNestedKeysAndRoot<T> = (
40-
// T extends object
41-
// ? {
42-
// [K in Exclude<keyof T, symbol>]: `${K}${DotPrefix<
43-
// DotNestedKeys<T[K]>
44-
// >}`;
45-
// }[Exclude<keyof T, symbol>]
46-
// : ''
47-
// ) extends infer D
48-
// ? Extract<D, string>
49-
// : never;
50-
5139
export type DotNestedKeysWithRoot<T> = (
5240
T extends object
5341
? {
5442
[K in Exclude<keyof T, symbol>]:
55-
| `${K}${DotPrefix<DotNestedKeys<T[K]>>}`
43+
| `${K}${DotPrefix<DotNestedKeysWithRoot<T[K]>>}`
5644
| `${K}`;
5745
}[Exclude<keyof T, symbol>]
5846
: ''
5947
) extends infer D
6048
? Extract<D, string>
6149
: never;
62-
6350
export type DotNestedKeys<T> = (
6451
T extends object
6552
? {

0 commit comments

Comments
 (0)