Skip to content

Commit 59a2aed

Browse files
authored
fix: RecursiveFormFields type for recursive or unknown schemas (#14734)
* fix: `RecursiveFormFields` type for recursive or unknown schemas * fix: better recursive unknown fields * simplify type
1 parent ef107ac commit 59a2aed

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

.changeset/upset-hotels-mix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: `RecursiveFormFields` type for recursive or unknown schemas

packages/kit/src/exports/public.d.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,22 +1914,46 @@ type RemoteFormFieldContainer<Value> = RemoteFormFieldMethods<Value> & {
19141914
allIssues(): RemoteFormIssue[] | undefined;
19151915
};
19161916

1917+
type UnknownField<Value> = RemoteFormFieldMethods<Value> & {
1918+
/** Validation issues belonging to this or any of the fields that belong to it, if any */
1919+
allIssues(): RemoteFormIssue[] | undefined;
1920+
/**
1921+
* Returns an object that can be spread onto an input element with the correct type attribute,
1922+
* aria-invalid attribute if the field is invalid, and appropriate value/checked property getters/setters.
1923+
* @example
1924+
* ```svelte
1925+
* <input {...myForm.fields.myString.as('text')} />
1926+
* <input {...myForm.fields.myNumber.as('number')} />
1927+
* <input {...myForm.fields.myBoolean.as('checkbox')} />
1928+
* ```
1929+
*/
1930+
as<T extends RemoteFormFieldType<Value>>(...args: AsArgs<T, Value>): InputElementProps<T>;
1931+
} & {
1932+
[key: string | number]: UnknownField<any>;
1933+
};
1934+
19171935
/**
19181936
* Recursive type to build form fields structure with proxy access
19191937
*/
1920-
type RemoteFormFields<T> =
1938+
export type RemoteFormFields<T> =
19211939
WillRecurseIndefinitely<T> extends true
19221940
? RecursiveFormFields
19231941
: NonNullable<T> extends string | number | boolean | File
19241942
? RemoteFormField<NonNullable<T>>
19251943
: T extends string[] | File[]
19261944
? RemoteFormField<T> & { [K in number]: RemoteFormField<T[number]> }
19271945
: T extends Array<infer U>
1928-
? RemoteFormFieldContainer<T> & { [K in number]: RemoteFormFields<U> }
1929-
: RemoteFormFieldContainer<T> & { [K in keyof T]-?: RemoteFormFields<T[K]> };
1946+
? RemoteFormFieldContainer<T> & {
1947+
[K in number]: RemoteFormFields<U>;
1948+
}
1949+
: RemoteFormFieldContainer<T> & {
1950+
[K in keyof T]-?: RemoteFormFields<T[K]>;
1951+
};
19301952

19311953
// By breaking this out into its own type, we avoid the TS recursion depth limit
1932-
type RecursiveFormFields = RemoteFormField<any> & { [key: string | number]: RecursiveFormFields };
1954+
type RecursiveFormFields = RemoteFormFieldContainer<any> & {
1955+
[key: string | number]: UnknownField<any>;
1956+
};
19331957

19341958
type MaybeArray<T> = T | T[];
19351959

packages/kit/types/index.d.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,22 +1890,46 @@ declare module '@sveltejs/kit' {
18901890
allIssues(): RemoteFormIssue[] | undefined;
18911891
};
18921892

1893+
type UnknownField<Value> = RemoteFormFieldMethods<Value> & {
1894+
/** Validation issues belonging to this or any of the fields that belong to it, if any */
1895+
allIssues(): RemoteFormIssue[] | undefined;
1896+
/**
1897+
* Returns an object that can be spread onto an input element with the correct type attribute,
1898+
* aria-invalid attribute if the field is invalid, and appropriate value/checked property getters/setters.
1899+
* @example
1900+
* ```svelte
1901+
* <input {...myForm.fields.myString.as('text')} />
1902+
* <input {...myForm.fields.myNumber.as('number')} />
1903+
* <input {...myForm.fields.myBoolean.as('checkbox')} />
1904+
* ```
1905+
*/
1906+
as<T extends RemoteFormFieldType<Value>>(...args: AsArgs<T, Value>): InputElementProps<T>;
1907+
} & {
1908+
[key: string | number]: UnknownField<any>;
1909+
};
1910+
18931911
/**
18941912
* Recursive type to build form fields structure with proxy access
18951913
*/
1896-
type RemoteFormFields<T> =
1914+
export type RemoteFormFields<T> =
18971915
WillRecurseIndefinitely<T> extends true
18981916
? RecursiveFormFields
18991917
: NonNullable<T> extends string | number | boolean | File
19001918
? RemoteFormField<NonNullable<T>>
19011919
: T extends string[] | File[]
19021920
? RemoteFormField<T> & { [K in number]: RemoteFormField<T[number]> }
19031921
: T extends Array<infer U>
1904-
? RemoteFormFieldContainer<T> & { [K in number]: RemoteFormFields<U> }
1905-
: RemoteFormFieldContainer<T> & { [K in keyof T]-?: RemoteFormFields<T[K]> };
1922+
? RemoteFormFieldContainer<T> & {
1923+
[K in number]: RemoteFormFields<U>;
1924+
}
1925+
: RemoteFormFieldContainer<T> & {
1926+
[K in keyof T]-?: RemoteFormFields<T[K]>;
1927+
};
19061928

19071929
// By breaking this out into its own type, we avoid the TS recursion depth limit
1908-
type RecursiveFormFields = RemoteFormField<any> & { [key: string | number]: RecursiveFormFields };
1930+
type RecursiveFormFields = RemoteFormFieldContainer<any> & {
1931+
[key: string | number]: UnknownField<any>;
1932+
};
19091933

19101934
type MaybeArray<T> = T | T[];
19111935

0 commit comments

Comments
 (0)