@strictly/react-form 0.0.17 → 0.0.19

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.
Files changed (38) hide show
  1. package/.out/core/mobx/form_model.d.ts +13 -10
  2. package/.out/core/mobx/form_model.js +103 -137
  3. package/.out/core/mobx/hooks.js +3 -4
  4. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +1 -1
  5. package/.out/core/mobx/merge_field_adapters_with_validators.js +5 -1
  6. package/.out/core/mobx/specs/form_model.tests.js +28 -23
  7. package/.out/core/props.d.ts +0 -2
  8. package/.out/mantine/create_fields_view.d.ts +2 -1
  9. package/.out/mantine/hooks.d.ts +6 -5
  10. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +5 -2
  11. package/.out/mantine/specs/checkbox_hooks.stories.js +3 -2
  12. package/.out/mantine/specs/text_input_hooks.stories.d.ts +3 -2
  13. package/.out/mantine/specs/text_input_hooks.stories.js +3 -2
  14. package/.out/mantine/types.d.ts +3 -3
  15. package/.out/tsconfig.tsbuildinfo +1 -1
  16. package/.out/util/partial.d.ts +5 -2
  17. package/.out/util/specs/partial.tests.d.ts +1 -0
  18. package/.out/util/specs/partial.tests.js +8 -0
  19. package/.turbo/turbo-build.log +8 -8
  20. package/.turbo/turbo-check-types.log +1 -1
  21. package/core/mobx/form_model.ts +97 -158
  22. package/core/mobx/hooks.tsx +6 -5
  23. package/core/mobx/merge_field_adapters_with_two_way_converter.ts +2 -1
  24. package/core/mobx/merge_field_adapters_with_validators.ts +1 -1
  25. package/core/mobx/specs/form_model.tests.ts +39 -27
  26. package/core/props.ts +0 -4
  27. package/dist/index.cjs +93 -139
  28. package/dist/index.d.cts +41 -35
  29. package/dist/index.d.ts +41 -35
  30. package/dist/index.js +92 -139
  31. package/mantine/create_fields_view.tsx +8 -4
  32. package/mantine/hooks.tsx +23 -15
  33. package/mantine/specs/checkbox_hooks.stories.tsx +7 -1
  34. package/mantine/specs/text_input_hooks.stories.tsx +8 -1
  35. package/mantine/types.ts +12 -4
  36. package/package.json +1 -1
  37. package/util/partial.tsx +8 -1
  38. package/util/specs/partial.tests.tsx +21 -0
@@ -1,7 +1,10 @@
1
1
  import { type FriendlyExhaustiveArrayOfUnion } from '@strictly/base';
2
- import { type ComponentProps, type ComponentType, type DependencyList, type ForwardRefExoticComponent, type PropsWithoutRef } from 'react';
2
+ import { type ComponentProps, type ComponentType, type DependencyList, type ForwardRefExoticComponent, type PropsWithoutRef, type Ref, type RefAttributes } from 'react';
3
+ export type RefOfProps<P, Fallback = unknown> = P extends RefAttributes<infer R> ? R : Fallback;
3
4
  export type PartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}> = Exclude<keyof CurriedProps, keyof ComponentProps<Component>> extends never ? UnsafePartialComponent<Component, CurriedProps, AdditionalProps> : keyof CurriedProps extends (string | number) ? `unmatched prop: ${Exclude<keyof CurriedProps, keyof ComponentProps<Component>>}` : Exclude<keyof CurriedProps, keyof ComponentProps<Component>>;
4
- export type UnsafePartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}> = ForwardRefExoticComponent<PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>>;
5
+ export type UnsafePartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}, R = RefOfProps<ComponentProps<Component>>> = ForwardRefExoticComponent<PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps> & {
6
+ ref?: Ref<R>;
7
+ }>;
5
8
  export declare function createSimplePartialComponent<Component extends ComponentType<any>, CurriedProps extends Partial<ComponentProps<Component>>>(Component: Component, curriedProps: CurriedProps): PartialComponent<Component, CurriedProps>;
6
9
  export declare function createPartialComponent<Component extends ComponentType<any>, CurriedProps>(Component: Component, curriedPropsSource: () => CurriedProps): PartialComponent<Component, CurriedProps, {}>;
7
10
  export declare function createPartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps, AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[]>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>): PartialComponent<Component, CurriedProps, AdditionalProps>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ describe('partial', () => {
2
+ describe('UnsafePartialComponent', () => {
3
+ it('allows a ref of a specific type to be passed', () => {
4
+ expectTypeOf().toEqualTypeOf();
5
+ });
6
+ });
7
+ });
8
+ export {};
@@ -7,12 +7,12 @@ $ tsup
7
7
  CLI Target: es6
8
8
  CJS Build start
9
9
  ESM Build start
10
- CJS dist/index.cjs 62.28 KB
11
- CJS ⚡️ Build success in 117ms
12
- ESM dist/index.js 58.27 KB
13
- ESM ⚡️ Build success in 140ms
10
+ CJS dist/index.cjs 60.71 KB
11
+ CJS ⚡️ Build success in 113ms
12
+ ESM dist/index.js 56.70 KB
13
+ ESM ⚡️ Build success in 113ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 9749ms
16
- DTS dist/index.d.cts 38.08 KB
17
- DTS dist/index.d.ts 38.08 KB
18
- Done in 10.88s.
15
+ DTS ⚡️ Build success in 9852ms
16
+ DTS dist/index.d.cts 38.50 KB
17
+ DTS dist/index.d.ts 38.50 KB
18
+ Done in 10.92s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc
3
- Done in 7.55s.
3
+ Done in 7.61s.
@@ -26,7 +26,6 @@ import {
26
26
  type ValueOfType,
27
27
  valuePathToTypePath,
28
28
  } from '@strictly/define'
29
- import { type FormMode } from 'core/props'
30
29
  import {
31
30
  computed,
32
31
  observable,
@@ -89,10 +88,15 @@ type FlattenedFieldOverrides<
89
88
  >
90
89
  }
91
90
 
92
- type FlattenedErrors<
91
+ export enum Validation {
92
+ Changed = 1,
93
+ Always = 2,
94
+ }
95
+
96
+ type FlattenedValidation<
93
97
  ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>,
94
98
  > = {
95
- -readonly [K in keyof ValuePathsToAdapters]?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>
99
+ -readonly [K in keyof ValuePathsToAdapters]?: Validation
96
100
  }
97
101
 
98
102
  export type ValuePathsToAdaptersOf<
@@ -119,6 +123,8 @@ export type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string
119
123
  | {}
120
124
  >
121
125
 
126
+ export type FormMode = 'edit' | 'create'
127
+
122
128
  export abstract class FormModel<
123
129
  T extends Type,
124
130
  ValueToTypePaths extends Readonly<Record<string, string>>,
@@ -137,16 +143,21 @@ export abstract class FormModel<
137
143
  @observable.shallow
138
144
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>
139
145
  @observable.shallow
140
- accessor errors: FlattenedErrors<ValuePathsToAdapters> = {}
146
+ accessor validation: FlattenedValidation<ValuePathsToAdapters> = {}
141
147
 
142
148
  private readonly flattenedTypeDefs: Readonly<Record<string, Type>>
143
149
 
150
+ // cannot be type safe
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ private readonly originalValues: Record<string, any>
153
+
144
154
  constructor(
145
155
  readonly type: T,
146
- private readonly originalValue: ValueOfType<ReadonlyTypeOfType<T>>,
156
+ originalValue: ValueOfType<ReadonlyTypeOfType<T>>,
147
157
  protected readonly adapters: TypePathsToAdapters,
148
158
  protected readonly mode: FormMode,
149
159
  ) {
160
+ this.originalValues = flattenValuesOfType<ReadonlyTypeOfType<T>>(type, originalValue)
150
161
  this.value = mobxCopy(type, originalValue)
151
162
  this.flattenedTypeDefs = flattenTypesOfType(type)
152
163
  // pre-populate field overrides for consistent behavior when default information is overwritten
@@ -270,6 +281,7 @@ export abstract class FormModel<
270
281
  const {
271
282
  convert,
272
283
  create,
284
+ revert,
273
285
  } = adapter
274
286
 
275
287
  const fieldOverride = this.fieldOverrides[valuePath]
@@ -277,6 +289,9 @@ export abstract class FormModel<
277
289
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
278
290
  const fieldTypeDef = this.flattenedTypeDefs[typePath as string]
279
291
  const context = this.toContext(this.value, valuePath)
292
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
293
+ const defaultValue = create(valuePath as string, context)
294
+
280
295
  const {
281
296
  value,
282
297
  required,
@@ -287,17 +302,50 @@ export abstract class FormModel<
287
302
  : fieldTypeDef != null
288
303
  ? mobxCopy(
289
304
  fieldTypeDef,
290
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
291
- create(valuePath as string, context),
305
+ defaultValue,
292
306
  )
293
307
  // fake values can't be copied
294
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
295
- : create(valuePath as string, context),
308
+ : defaultValue,
296
309
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
297
310
  valuePath as string,
298
311
  context,
299
312
  )
300
- const error = this.errors[valuePath]
313
+ // const error = this.errors[valuePath]
314
+ let error: unknown = undefined
315
+ const displayedValue = fieldOverride != null ? fieldOverride[0] : value
316
+ const validation = this.validation[valuePath]
317
+ switch (validation) {
318
+ case undefined:
319
+ // skip validation
320
+ break
321
+ case Validation.Changed:
322
+ if (revert != null) {
323
+ const originalValue = valuePath in this.originalValues
324
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
325
+ ? this.originalValues[valuePath as string]
326
+ : defaultValue
327
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
328
+ const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
329
+ // TODO better comparisons, displayed values can still be complex
330
+ if (displayedValue !== originalDisplayedValue) {
331
+ const revertResult = revert(displayedValue, valuePath, context)
332
+ if (revertResult?.type === UnreliableFieldConversionType.Failure) {
333
+ ;({ error } = revertResult)
334
+ }
335
+ }
336
+ }
337
+ break
338
+ case Validation.Always:
339
+ {
340
+ const revertResult = revert?.(displayedValue, valuePath, context)
341
+ if (revertResult?.type === UnreliableFieldConversionType.Failure) {
342
+ ;({ error } = revertResult)
343
+ }
344
+ }
345
+ break
346
+ default:
347
+ throw new UnreachableError(validation)
348
+ }
301
349
  return {
302
350
  value: fieldOverride != null ? fieldOverride[0] : value,
303
351
  error,
@@ -342,18 +390,12 @@ export abstract class FormModel<
342
390
  return valuePathToTypePath<ValueToTypePaths, K>(this.type, valuePath, true)
343
391
  }
344
392
 
345
- setFieldValueAndValidate<K extends keyof ValuePathsToAdapters>(
346
- valuePath: K,
347
- value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
348
- ): boolean {
349
- return this.internalSetFieldValue(valuePath, value, true)
350
- }
351
-
352
393
  setFieldValue<K extends keyof ValuePathsToAdapters>(
353
394
  valuePath: K,
354
395
  value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
396
+ validation: Validation | undefined | null = this.validation[valuePath],
355
397
  ): boolean {
356
- return this.internalSetFieldValue(valuePath, value, false)
398
+ return this.internalSetFieldValue(valuePath, value, validation)
357
399
  }
358
400
 
359
401
  addListItem<K extends keyof FlattenedListTypesOfType<T>>(
@@ -429,9 +471,9 @@ export abstract class FormModel<
429
471
  const fieldOverride = this.fieldOverrides[fromJsonPath]
430
472
  delete this.fieldOverrides[fromJsonPath]
431
473
  this.fieldOverrides[toJsonPath] = fieldOverride
432
- const error = this.errors[fromJsonPath]
433
- delete this.errors[fromJsonPath]
434
- this.errors[toJsonPath] = error
474
+ const validation = this.validation[fromJsonPath]
475
+ delete this.validation[fromJsonPath]
476
+ this.validation[toJsonPath] = validation
435
477
  })
436
478
  accessor.set(newList)
437
479
  // delete any value overrides so the new list isn't shadowed
@@ -506,9 +548,9 @@ export abstract class FormModel<
506
548
  const fieldOverride = this.fieldOverrides[fromJsonPath]
507
549
  delete this.fieldOverrides[fromJsonPath]
508
550
  this.fieldOverrides[toJsonPath] = fieldOverride
509
- const error = this.errors[fromJsonPath]
510
- delete this.errors[fromJsonPath]
511
- this.errors[toJsonPath] = error
551
+ const validation = this.validation[fromJsonPath]
552
+ delete this.validation[fromJsonPath]
553
+ this.validation[toJsonPath] = validation
512
554
  })
513
555
  accessor.set(newList)
514
556
  // delete any value overrides so the new list isn't shadowed
@@ -521,7 +563,7 @@ export abstract class FormModel<
521
563
  private internalSetFieldValue<K extends keyof ValuePathsToAdapters>(
522
564
  valuePath: K,
523
565
  value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
524
- displayValidation: boolean,
566
+ validation: Validation | undefined | null,
525
567
  ): boolean {
526
568
  const { revert } = this.getAdapterForValuePath(valuePath)
527
569
 
@@ -532,17 +574,18 @@ export abstract class FormModel<
532
574
  const accessor = this.getAccessorForValuePath(valuePath)
533
575
  return runInAction(() => {
534
576
  this.fieldOverrides[valuePath] = [value]
577
+ if (validation != null) {
578
+ this.validation[valuePath] = validation
579
+ } else {
580
+ delete this.validation[valuePath]
581
+ }
535
582
  switch (conversion.type) {
536
583
  case UnreliableFieldConversionType.Failure:
537
- if (displayValidation) {
538
- this.errors[valuePath] = conversion.error
539
- }
540
584
  if (conversion.value != null && accessor != null) {
541
585
  accessor.set(conversion.value[0])
542
586
  }
543
587
  return false
544
588
  case UnreliableFieldConversionType.Success:
545
- delete this.errors[valuePath]
546
589
  accessor?.set(conversion.value)
547
590
  return true
548
591
  default:
@@ -555,12 +598,12 @@ export abstract class FormModel<
555
598
  const fieldOverride = this.fieldOverrides[valuePath]
556
599
  if (fieldOverride != null) {
557
600
  runInAction(() => {
558
- delete this.errors[valuePath]
601
+ delete this.validation[valuePath]
559
602
  })
560
603
  }
561
604
  }
562
605
 
563
- clearFieldValue<K extends StringKeyOf<ValueToTypePaths>>(valuePath: K) {
606
+ clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K) {
564
607
  const typePath = this.typePath(valuePath)
565
608
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
566
609
  const adapter = this.adapters[typePath as keyof TypePathsToAdapters]
@@ -581,12 +624,13 @@ export abstract class FormModel<
581
624
  const key = valuePath as unknown as keyof ValuePathsToAdapters
582
625
  runInAction(() => {
583
626
  this.fieldOverrides[key] = [displayValue]
627
+ delete this.validation[key]
584
628
  })
585
629
  }
586
630
 
587
631
  clearAll(value: ValueOfType<T>): void {
588
632
  runInAction(() => {
589
- this.errors = {}
633
+ this.validation = {}
590
634
  // TODO this isn't correct, should reload from value
591
635
  this.fieldOverrides = {}
592
636
  this.value = mobxCopy(this.type, value)
@@ -602,137 +646,32 @@ export abstract class FormModel<
602
646
 
603
647
  validateField<K extends keyof ValuePathsToAdapters>(
604
648
  valuePath: K,
605
- ignoreDefaultValue = false,
649
+ validation: Validation = Math.max(
650
+ this.mode === 'create' ? Validation.Always : Validation.Changed,
651
+ this.validation[valuePath] ?? Validation.Changed,
652
+ ),
606
653
  ): boolean {
607
- const {
608
- convert,
609
- revert,
610
- create,
611
- } = this.getAdapterForValuePath(valuePath)
612
- const fieldOverride = this.fieldOverrides[valuePath]
613
- const accessor = this.getAccessorForValuePath(valuePath)
614
- const context = this.toContext(this.value, valuePath)
615
-
616
- const {
617
- value: storedValue,
618
- } = convert(
619
- accessor != null
620
- ? accessor.value
621
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
622
- : create(valuePath as string, context),
623
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
624
- valuePath as string,
625
- context,
626
- )
627
- const value = fieldOverride != null
628
- ? fieldOverride[0]
629
- : storedValue
630
- const dirty = storedValue !== value
631
- assertExists(revert, 'changing field directly not supported {}', valuePath)
632
- if (ignoreDefaultValue) {
633
- const {
634
- value: defaultDisplayValue,
635
- } = convert(create(valuePath, context), valuePath, context)
636
- if (defaultDisplayValue === value) {
637
- return true
638
- }
639
- }
640
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
641
- const conversion = revert(value, valuePath as string, context)
642
- return runInAction(() => {
643
- switch (conversion.type) {
644
- case UnreliableFieldConversionType.Failure:
645
- this.errors[valuePath] = conversion.error
646
- if (conversion.value != null && accessor != null && dirty) {
647
- accessor.set(conversion.value[0])
648
- }
649
- return false
650
- case UnreliableFieldConversionType.Success:
651
- delete this.errors[valuePath]
652
- if (accessor != null && dirty) {
653
- accessor.set(conversion.value)
654
- }
655
- return true
656
- default:
657
- throw new UnreachableError(conversion)
658
- }
654
+ runInAction(() => {
655
+ this.validation[valuePath] = validation
659
656
  })
657
+ return this.fields[valuePath].error == null
660
658
  }
661
659
 
662
- validateAll(force: boolean = this.mode === 'create'): boolean {
663
- // sort keys shortest to longest so parent changes don't overwrite child changes
664
- const accessors = toArray(this.accessors).toSorted(function ([a], [b]) {
665
- return a.length - b.length
666
- })
667
-
668
- const flattenedOriginalValues = flattenValuesOfType(this.type, this.originalValue)
660
+ validateAll(validation: Validation = this.mode === 'create' ? Validation.Always : Validation.Changed): boolean {
661
+ const accessors = toArray(this.accessors)
669
662
 
670
- return runInAction(() => {
671
- return accessors.reduce(
672
- (
673
- success,
674
- [
675
- valuePath,
676
- accessor,
677
- ],
678
- ): boolean => {
679
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
680
- const adapterPath = valuePath as keyof ValuePathsToAdapters
681
- const adapter = this.maybeGetAdapterForValuePath(adapterPath)
682
- if (adapter == null) {
683
- // no adapter == there should be nothing specified for this field
684
- return success
685
- }
686
- const {
687
- convert,
688
- revert,
689
- } = adapter
690
- if (revert == null) {
691
- // no convert method means this field is immutable
692
- return success
693
- }
694
- const fieldOverride = this.fieldOverrides[adapterPath]
695
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
696
- const context = this.toContext(this.value, valuePath as keyof ValuePathsToAdapters)
697
- const {
698
- value: storedValue,
699
- } = convert(accessor.value, valuePath, context)
700
- const value = fieldOverride != null
701
- ? fieldOverride[0]
702
- : storedValue
703
- // TODO customizable comparisons
704
- const dirty = fieldOverride != null && fieldOverride[0] !== storedValue
705
- const needsValidation = force
706
- || !(valuePath in flattenedOriginalValues)
707
- || storedValue !== convert(
708
- flattenedOriginalValues[valuePath],
709
- valuePath,
710
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
711
- this.toContext(this.originalValue, valuePath as keyof ValuePathsToAdapters),
712
- ).value
713
- if (needsValidation) {
714
- const conversion = revert(value, valuePath, context)
715
- switch (conversion.type) {
716
- case UnreliableFieldConversionType.Failure:
717
- this.errors[adapterPath] = conversion.error
718
- if (conversion.value != null && dirty) {
719
- accessor.set(conversion.value[0])
720
- }
721
- return false
722
- case UnreliableFieldConversionType.Success:
723
- if (dirty) {
724
- accessor.set(conversion.value)
725
- }
726
- delete this.errors[adapterPath]
727
- return success
728
- default:
729
- throw new UnreachableError(conversion)
730
- }
731
- }
732
- return success
733
- },
734
- true,
735
- )
663
+ runInAction(() => {
664
+ accessors.forEach(([valuePath]) => {
665
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
666
+ this.validation[valuePath as keyof ValuePathsToAdapters] = validation
667
+ })
736
668
  })
669
+ return accessors.every(
670
+ ([valuePath]): boolean => {
671
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
672
+ const field = this.fields[valuePath as keyof ValuePathsToAdapters]
673
+ return field?.error == null
674
+ },
675
+ )
737
676
  }
738
677
  }
@@ -5,7 +5,9 @@ import {
5
5
  import {
6
6
  useCallback,
7
7
  } from 'react'
8
- import { type FormModel } from './form_model'
8
+ import {
9
+ type FormModel,
10
+ } from './form_model'
9
11
  import {
10
12
  type FormFieldsOfModel,
11
13
  type ToValueOfModelValuePath,
@@ -42,9 +44,8 @@ export function useDefaultMobxFormHooks<
42
44
  path: Path,
43
45
  value: ToValueOfModelValuePath<M, Path>,
44
46
  ) {
45
- // TODO do in one action
46
- model.clearFieldError(path)
47
- model.setFieldValue<Path>(path, value)
47
+ // clear any validation
48
+ model.setFieldValue<Path>(path, value, null)
48
49
  },
49
50
  [model],
50
51
  )
@@ -69,7 +70,7 @@ export function useDefaultMobxFormHooks<
69
70
  // TODO debounce?
70
71
  setTimeout(function () {
71
72
  if (model.isValuePathActive(path)) {
72
- model.validateField(path, true)
73
+ model.validateField(path)
73
74
  }
74
75
  }, 100)
75
76
  },
@@ -40,13 +40,14 @@ export function mergeFieldAdaptersWithTwoWayConverter<
40
40
  FieldAdapters extends Readonly<Record<string, FieldAdapter>>,
41
41
  E,
42
42
  Context,
43
+ P extends ValuePathsOfFieldAdapters<FieldAdapters>,
43
44
  >(
44
45
  fieldAdapters: FieldAdapters,
45
46
  converter: TwoWayFieldConverter<
46
47
  TosOfFieldAdapters<FieldAdapters>,
47
48
  TosOfFieldAdapters<FieldAdapters>,
48
49
  E,
49
- ValuePathsOfFieldAdapters<FieldAdapters>,
50
+ P,
50
51
  Context
51
52
  >,
52
53
  ): MergedOfFieldAdaptersWithTwoWayConverter<FieldAdapters, E, Context> {
@@ -88,7 +88,7 @@ export function mergeAdaptersWithValidators<
88
88
  }
89
89
  }
90
90
  acc[key] = {
91
- ...adapter,
91
+ create: adapter.create.bind(adapter),
92
92
  convert,
93
93
  revert: adapter.revert && revert,
94
94
  }