Skip to content

Commit 0e75f47

Browse files
committed
feat: Add TS Support for renderByDefault to Integrations
Technically, the `renderByDefault` feature was already working in the framework integration packages. But this change makes sure that _TypeScript_ knows that as well. No one wants their type checker yelling at them for invalid reasons. NOTE: This commit ALSO fixes an uncaught bug where `useFormValidityObserver` wouldn't create a new instance of the `FormValidityObserver` when the `defaultErrors` option changed between state updates.
1 parent 8607b07 commit 0e75f47

12 files changed

+164
-111
lines changed

packages/lit/createFormValidityObserver.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import FormValidityObserver from "@form-observer/core/FormValidityObserver";
66
* @template {import("./index.d.ts").OneOrMany<import("./index.d.ts").EventType>} T
77
* @template [M=string]
88
* @template {import("./index.d.ts").ValidatableField} [E=import("./index.d.ts").ValidatableField]
9+
* @template {boolean} [R=false]
910
* @param {T} types
10-
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E>} [options]
11-
* @returns {FormValidityObserver<M>}
11+
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E, R>} [options]
12+
* @returns {FormValidityObserver<M, R>}
1213
*/
1314
export default function createFormValidityObserver(types, options) {
1415
const observer = new FormValidityObserver(types, options);

packages/preact/createFormValidityObserver.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import FormValidityObserver from "@form-observer/core/FormValidityObserver";
66
* @template {import("./index.d.ts").OneOrMany<import("./index.d.ts").EventType>} T
77
* @template [M=string]
88
* @template {import("./index.d.ts").ValidatableField} [E=import("./index.d.ts").ValidatableField]
9+
* @template {boolean} [R=false]
910
* @param {T} types
10-
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E>} [options]
11-
* @returns {import("./types.d.ts").PreactFormValidityObserver<M>}
11+
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E, R>} [options]
12+
* @returns {import("./types.d.ts").PreactFormValidityObserver<M, R>}
1213
*/
1314
export default function createFormValidityObserver(types, options) {
14-
const observer = /** @type {import("./types.d.ts").PreactFormValidityObserver<M>} */ (
15+
const observer = /** @type {import("./types.d.ts").PreactFormValidityObserver<M, R>} */ (
1516
/** @type {unknown} */ (new FormValidityObserver(types, options))
1617
);
1718

@@ -28,7 +29,7 @@ export default function createFormValidityObserver(types, options) {
2829
observer.clearFieldError = observer.clearFieldError.bind(observer);
2930

3031
/** **Private** reference to the original {@link FormValidityObserver.configure} method */
31-
const originalConfigure = /** @type {FormValidityObserver<M>["configure"]} */ (observer.configure.bind(observer));
32+
const originalConfigure = /** @type {FormValidityObserver<M, R>["configure"]} */ (observer.configure.bind(observer));
3233

3334
/* -------------------- Enhancements -------------------- */
3435
// Add automatic setup/teardown
@@ -52,11 +53,11 @@ export default function createFormValidityObserver(types, options) {
5253

5354
// Enhanced `configure` method
5455
observer.configure = function configure(name, errorMessages) {
55-
const keys = /** @type {Array<keyof import("./types.d.ts").PreactValidationErrors<M>>} */ (
56+
const keys = /** @type {Array<keyof import("./types.d.ts").PreactValidationErrors<M, any, R>>} */ (
5657
Object.keys(errorMessages)
5758
);
5859
const props = /** @type {import("./types.d.ts").PreactFieldProps} */ ({ name });
59-
const config = /** @type {import("./index.d.ts").ValidationErrors<M>} */ ({});
60+
const config = /** @type {import("./index.d.ts").ValidationErrors<M, any, R>} */ ({});
6061

6162
// Build `props` object and error `config` object from `errorMessages`
6263
for (let i = 0; i < keys.length; i++) {

packages/preact/types.d.ts

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { ErrorMessage, ValidationErrors, ValidatableField, FormValidityObserver } from "@form-observer/core";
22
import type { JSX } from "preact";
33

4-
export interface PreactFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
4+
export interface PreactFormValidityObserver<M = string, R extends boolean = false>
5+
extends Omit<FormValidityObserver<M, R>, "configure"> {
56
/**
67
* An enhanced version of {@link FormValidityObserver.configure} for `Preact`. In addition to configuring a field's
78
* error messages, it generates the props that should be applied to the field based on the provided arguments.
@@ -21,7 +22,7 @@ export interface PreactFormValidityObserver<M = string> extends Omit<FormValidit
2122
* <input {...configure("comment", { maxlength: 10 })} />
2223
* <input name="another-comment" maxlength={10} />
2324
*/
24-
configure<E extends ValidatableField>(name: string, errorMessages: PreactValidationErrors<M, E>): PreactFieldProps;
25+
configure<E extends ValidatableField>(name: string, errorMessages: PreactValidationErrors<M, E, R>): PreactFieldProps;
2526

2627
/**
2728
* Creates a Preact `ref` callback used to automatically setup and cleanup a form's observer.
@@ -54,21 +55,28 @@ export type PreactFieldProps = Pick<
5455
* An augmetation of {@link ValidationErrors} for `Preact`. Represents the constraints that should be applied
5556
* to a form field, and the error messages that should be displayed when those constraints are broken.
5657
*/
57-
export interface PreactValidationErrors<M, E extends ValidatableField = ValidatableField>
58-
extends Pick<ValidationErrors<M, E>, "badinput" | "validate"> {
58+
export interface PreactValidationErrors<M, E extends ValidatableField = ValidatableField, R extends boolean = false>
59+
extends Pick<ValidationErrors<M, E, R>, "badinput" | "validate"> {
5960
// Standard HTML Attributes
60-
required?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["required"], E> | ErrorMessage<string, E>;
61-
minlength?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["minlength"], E>;
62-
min?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["min"], E>;
63-
maxlength?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["maxlength"], E>;
64-
max?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["max"], E>;
65-
step?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["step"], E>;
66-
type?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["type"], E>;
67-
pattern?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["pattern"], E>;
61+
required?:
62+
| PreactErrorDetails<M, JSX.IntrinsicElements["input"]["required"], E, R>
63+
| ErrorMessage<R extends true ? M : string, E>;
64+
minlength?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["minlength"], E, R>;
65+
min?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["min"], E, R>;
66+
maxlength?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["maxlength"], E, R>;
67+
max?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["max"], E, R>;
68+
step?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["step"], E, R>;
69+
type?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["type"], E, R>;
70+
pattern?: PreactErrorDetails<M, JSX.IntrinsicElements["input"]["pattern"], E, R>;
6871
}
6972

7073
/** An augmentation of the core `ErrorDetails` type for `Preact`. */
71-
export type PreactErrorDetails<M, V, E extends ValidatableField = ValidatableField> =
74+
export type PreactErrorDetails<M, V, E extends ValidatableField = ValidatableField, R extends boolean = false> =
7275
| V
73-
| { render: true; message: ErrorMessage<M, E>; value: V }
74-
| { render?: false; message: ErrorMessage<string, E>; value: V };
76+
| (R extends true
77+
?
78+
| { render?: true; message: ErrorMessage<M, E>; value: V }
79+
| { render: false; message: ErrorMessage<string, E>; value: V }
80+
:
81+
| { render: true; message: ErrorMessage<M, E>; value: V }
82+
| { render?: false; message: ErrorMessage<string, E>; value: V });

packages/react/createFormValidityObserver.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ const constraintsMap = Object.freeze({
2424
* @template {import("./index.d.ts").OneOrMany<import("./index.d.ts").EventType>} T
2525
* @template [M=string]
2626
* @template {import("./index.d.ts").ValidatableField} [E=import("./index.d.ts").ValidatableField]
27+
* @template {boolean} [R=false]
2728
* @param {T} types
28-
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E>} [options]
29-
* @returns {import("./types.d.ts").ReactFormValidityObserver<M>}
29+
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E, R>} [options]
30+
* @returns {import("./types.d.ts").ReactFormValidityObserver<M, R>}
3031
*/
3132
export default function createFormValidityObserver(types, options) {
32-
const observer = /** @type {import("./types.d.ts").ReactFormValidityObserver<M>} */ (
33+
const observer = /** @type {import("./types.d.ts").ReactFormValidityObserver<M, R>} */ (
3334
/** @type {unknown} */ (new FormValidityObserver(types, options))
3435
);
3536

@@ -46,7 +47,7 @@ export default function createFormValidityObserver(types, options) {
4647
observer.clearFieldError = observer.clearFieldError.bind(observer);
4748

4849
/** **Private** reference to the original `FormValidityObserver.configure` method */
49-
const originalConfigure = /** @type {FormValidityObserver<M>["configure"]} */ (observer.configure.bind(observer));
50+
const originalConfigure = /** @type {FormValidityObserver<M, R>["configure"]} */ (observer.configure.bind(observer));
5051

5152
/* -------------------- Enhancements -------------------- */
5253
// Add automatic setup/teardown
@@ -70,11 +71,11 @@ export default function createFormValidityObserver(types, options) {
7071

7172
// Enhanced `configure` method
7273
observer.configure = function configure(name, errorMessages) {
73-
const keys = /** @type {Array<keyof import("./types.d.ts").ReactValidationErrors<M>>} */ (
74+
const keys = /** @type {Array<keyof import("./types.d.ts").ReactValidationErrors<M, any, R>>} */ (
7475
Object.keys(errorMessages)
7576
);
7677
const props = /** @type {import("./types.d.ts").ReactFieldProps} */ ({ name });
77-
const config = /** @type {import("./index.d.ts").ValidationErrors<M>} */ ({});
78+
const config = /** @type {import("./index.d.ts").ValidationErrors<M, any, R>} */ ({});
7879

7980
// Build `props` object and error `config` object from `errorMessages`
8081
for (let i = 0; i < keys.length; i++) {

packages/react/types.d.ts

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { ErrorMessage, ValidationErrors, ValidatableField, FormValidityObserver } from "@form-observer/core";
22
import type React from "react";
33

4-
export interface ReactFormValidityObserver<M = string> extends Omit<FormValidityObserver<M>, "configure"> {
4+
export interface ReactFormValidityObserver<M = string, R extends boolean = false>
5+
extends Omit<FormValidityObserver<M, R>, "configure"> {
56
/**
67
* An enhanced version of {@link FormValidityObserver.configure} for `React`. In addition to configuring a field's
78
* error messages, it generates the props that should be applied to the field based on the provided arguments.
@@ -21,7 +22,7 @@ export interface ReactFormValidityObserver<M = string> extends Omit<FormValidity
2122
* <input {...configure("comment", { maxlength: 10 })} />
2223
* <input name="another-comment" maxLength={10} />
2324
*/
24-
configure<E extends ValidatableField>(name: string, errorMessages: ReactValidationErrors<M, E>): ReactFieldProps;
25+
configure<E extends ValidatableField>(name: string, errorMessages: ReactValidationErrors<M, E, R>): ReactFieldProps;
2526

2627
/**
2728
* Creates a React `ref` callback used to automatically setup and cleanup a form's observer.
@@ -54,21 +55,28 @@ export type ReactFieldProps = Pick<
5455
* An augmetation of {@link ValidationErrors} for `React`. Represents the constraints that should be applied
5556
* to a form field, and the error messages that should be displayed when those constraints are broken.
5657
*/
57-
export interface ReactValidationErrors<M, E extends ValidatableField = ValidatableField>
58-
extends Pick<ValidationErrors<M, E>, "badinput" | "validate"> {
58+
export interface ReactValidationErrors<M, E extends ValidatableField = ValidatableField, R extends boolean = false>
59+
extends Pick<ValidationErrors<M, E, R>, "badinput" | "validate"> {
5960
// Standard HTML Attributes
60-
required?: ReactErrorDetails<M, React.ComponentProps<"input">["required"], E> | ErrorMessage<string, E>;
61-
minlength?: ReactErrorDetails<M, React.ComponentProps<"input">["minLength"], E>;
62-
min?: ReactErrorDetails<M, React.ComponentProps<"input">["min"], E>;
63-
maxlength?: ReactErrorDetails<M, React.ComponentProps<"input">["maxLength"], E>;
64-
max?: ReactErrorDetails<M, React.ComponentProps<"input">["max"], E>;
65-
step?: ReactErrorDetails<M, React.ComponentProps<"input">["step"], E>;
66-
type?: ReactErrorDetails<M, React.ComponentProps<"input">["type"], E>;
67-
pattern?: ReactErrorDetails<M, React.ComponentProps<"input">["pattern"], E>;
61+
required?:
62+
| ReactErrorDetails<M, React.ComponentProps<"input">["required"], E, R>
63+
| ErrorMessage<R extends true ? M : string, E>;
64+
minlength?: ReactErrorDetails<M, React.ComponentProps<"input">["minLength"], E, R>;
65+
min?: ReactErrorDetails<M, React.ComponentProps<"input">["min"], E, R>;
66+
maxlength?: ReactErrorDetails<M, React.ComponentProps<"input">["maxLength"], E, R>;
67+
max?: ReactErrorDetails<M, React.ComponentProps<"input">["max"], E, R>;
68+
step?: ReactErrorDetails<M, React.ComponentProps<"input">["step"], E, R>;
69+
type?: ReactErrorDetails<M, React.ComponentProps<"input">["type"], E, R>;
70+
pattern?: ReactErrorDetails<M, React.ComponentProps<"input">["pattern"], E, R>;
6871
}
6972

7073
/** An augmentation of the core `ErrorDetails` type for `React`. */
71-
export type ReactErrorDetails<M, V, E extends ValidatableField = ValidatableField> =
74+
export type ReactErrorDetails<M, V, E extends ValidatableField = ValidatableField, R extends boolean = false> =
7275
| V
73-
| { render: true; message: ErrorMessage<M, E>; value: V }
74-
| { render?: false; message: ErrorMessage<string, E>; value: V };
76+
| (R extends true
77+
?
78+
| { render?: true; message: ErrorMessage<M, E>; value: V }
79+
| { render: false; message: ErrorMessage<string, E>; value: V }
80+
:
81+
| { render: true; message: ErrorMessage<M, E>; value: V }
82+
| { render?: false; message: ErrorMessage<string, E>; value: V });

packages/react/useFormValidityObserver.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,20 @@ import createFormValidityObserver from "./createFormValidityObserver.js";
77
* @template {import("./index.d.ts").OneOrMany<import("./index.d.ts").EventType>} T
88
* @template [M=string]
99
* @template {import("./index.d.ts").ValidatableField} [E=import("./index.d.ts").ValidatableField]
10+
* @template {boolean} [R=false]
1011
* @param {T} types
11-
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E>} [options]
12-
* @returns {import("./types.d.ts").ReactFormValidityObserver<M>}
12+
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E, R>} [options]
13+
* @returns {import("./types.d.ts").ReactFormValidityObserver<M, R>}
1314
*/
1415
export default function useFormValidityObserver(types, options) {
1516
return useMemo(() => {
1617
return createFormValidityObserver(types, options);
17-
}, [types, options?.useEventCapturing, options?.scroller, options?.renderer]);
18+
}, [
19+
types,
20+
options?.useEventCapturing,
21+
options?.scroller,
22+
options?.renderer,
23+
options?.renderByDefault,
24+
options?.defaultErrors,
25+
]);
1826
}

packages/solid/createFormValidityObserver.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import { onMount, onCleanup } from "solid-js";
77
* @template {import("./index.d.ts").OneOrMany<import("./index.d.ts").EventType>} T
88
* @template [M=string | import("solid-js").JSX.Element]
99
* @template {import("./index.d.ts").ValidatableField} [E=import("./index.d.ts").ValidatableField]
10+
* @template {boolean} [R=false]
1011
* @param {T} types
11-
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E>} [options]
12-
* @returns {import("./types.d.ts").SolidFormValidityObserver<M>}
12+
* @param {import("./index.d.ts").FormValidityObserverOptions<M, E, R>} [options]
13+
* @returns {import("./types.d.ts").SolidFormValidityObserver<M, R>}
1314
*/
1415
export default function createFormValidityObserver(types, options) {
1516
const augmentedOptions = /** @type {typeof options} */ ({ renderer: defaultErrorRendererSolid, ...options });
1617

17-
const observer = /** @type {import("./types.d.ts").SolidFormValidityObserver<M>} */ (
18+
const observer = /** @type {import("./types.d.ts").SolidFormValidityObserver<M, R>} */ (
1819
/** @type {unknown} */ (new FormValidityObserver(types, augmentedOptions))
1920
);
2021

@@ -31,7 +32,7 @@ export default function createFormValidityObserver(types, options) {
3132
observer.clearFieldError = observer.clearFieldError.bind(observer);
3233

3334
/** **Private** reference to the original {@link FormValidityObserver.configure} method */
34-
const originalConfigure = /** @type {FormValidityObserver<M>["configure"]} */ (observer.configure.bind(observer));
35+
const originalConfigure = /** @type {FormValidityObserver<M, R>["configure"]} */ (observer.configure.bind(observer));
3536

3637
/* -------------------- Enhancements -------------------- */
3738
// Add automatic setup/teardown
@@ -43,11 +44,11 @@ export default function createFormValidityObserver(types, options) {
4344

4445
// Enhanced `configure` method
4546
observer.configure = function configure(name, errorMessages) {
46-
const keys = /** @type {Array<keyof import("./types.d.ts").SolidValidationErrors<M>>} */ (
47+
const keys = /** @type {Array<keyof import("./types.d.ts").SolidValidationErrors<M, any, R>>} */ (
4748
Object.keys(errorMessages)
4849
);
4950
const props = /** @type {import("./types.d.ts").SolidFieldProps} */ ({ name });
50-
const config = /** @type {import("./index.d.ts").ValidationErrors<M>} */ ({});
51+
const config = /** @type {import("./index.d.ts").ValidationErrors<M, any, R>} */ ({});
5152

5253
// Build `props` object and error `config` object from `errorMessages`
5354
for (let i = 0; i < keys.length; i++) {

0 commit comments

Comments
 (0)