Skip to content

Commit

Permalink
feat(core): add ExtractFormValues type (#45)
Browse files Browse the repository at this point in the history
- add ExtractFormValues type
- make FormSchema into nominal type

Co-authored-by: Mikołaj Klaman <mklaman@virtuslab.com>
  • Loading branch information
mixvar and Mikołaj Klaman authored Nov 29, 2020
1 parent f8a8a47 commit 6f4d16f
Show file tree
Hide file tree
Showing 9 changed files with 39 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/core/helpers/create-initial-values.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { createInitialValues } from "./create-initial-values";

describe("createInitialValues", () => {
it("should be empty for empty schema", () => {
expect(createInitialValues({})).toEqual({});
const schema = createFormSchema(() => ({}));

expect(createInitialValues(schema)).toEqual({});
});

it("for one-element schema", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/core/helpers/create-initial-values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const createInitialValues = <Values extends object>(
): Values => {
const initialStateFromDecoders = entries(schema).reduce(
(shape, [key, descriptor]) => {
shape[key] = impl(descriptor).__decoder.init();
shape[key] = impl(descriptor).__decoder.init() as Values[keyof Values];
return shape;
},
{} as Values
Expand Down
2 changes: 1 addition & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ export { FormtsOptions } from "./types/formts-options";
export { FieldDecoder } from "./types/field-decoder";
export { FieldDescriptor } from "./types/field-descriptor";
export { FieldHandle } from "./types/field-handle";
export { FormSchema } from "./types/form-schema";
export { FormSchema, ExtractFormValues } from "./types/form-schema";
export { FormValidator, Validator } from "./types/form-validator";
export { FormController } from "./types/form-controller";
2 changes: 1 addition & 1 deletion src/core/types/field-decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const isArrayDecoder = <T>(
* Should be used together with `createForm.schema` function.
*/
// @ts-ignore
export interface FieldDecoder<T> extends Nominal<"FieldDecoder", {}> {}
export interface FieldDecoder<T> extends Nominal<"FieldDecoder"> {}

export type FieldType =
| "number"
Expand Down
2 changes: 1 addition & 1 deletion src/core/types/field-descriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type _NTHHandler<T> = {
*/
// @ts-ignore
export interface FieldDescriptor<T, Err = unknown>
extends Nominal<"FieldDescriptor", {}> {}
extends Nominal<"FieldDescriptor"> {}

// prettier-ignore
export type GenericFieldDescriptor<T, Err = unknown> =
Expand Down
2 changes: 1 addition & 1 deletion src/core/types/form-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { InternalFormtsContext } from "./formts-context";
/**
* Used to connect together hooks or FormProvider component with the actual form state
*/
export interface FormController extends Nominal<"FormControl", {}> {}
export interface FormController extends Nominal<"FormControl"> {}

export type _FormControllerImpl<Values extends object, Err> = {
__ctx: InternalFormtsContext<Values, Err>;
Expand Down
20 changes: 19 additions & 1 deletion src/core/types/form-schema.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IsExact, assert } from "conditional-type-checks";

import { FieldDescriptor } from "./field-descriptor";
import { FormSchema } from "./form-schema";
import { FormSchema, ExtractFormValues } from "./form-schema";

type SomeValues = {
string: string;
Expand Down Expand Up @@ -158,3 +158,21 @@ describe("FormSchema type", () => {
assert<IsExact<Actual, Expected>>(true);
});
});

describe("ExtractFormValues type", () => {
it("extracts type of form values out of FormSchema type", () => {
type Schema = FormSchema<SomeValues, SomeErr>;

type Actual = ExtractFormValues<Schema>;
type Expected = SomeValues;

assert<IsExact<Actual, Expected>>(true);
});

it("resolves to never for invalid input", () => {
type Actual = ExtractFormValues<{}>;
type Expected = never;

assert<IsExact<Actual, Expected>>(true);
});
});
13 changes: 10 additions & 3 deletions src/core/types/form-schema.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { Nominal } from "../../utils";

import { GenericFieldDescriptor } from "./field-descriptor";

/**
* Description of a form.
* Used to interact with Formts API and point to specific form fields.
*/
export type FormSchema<Values extends object, Err> = {
readonly [K in keyof Values]: GenericFieldDescriptor<Values[K], Err>;
};
export type FormSchema<Values extends object, Err> = Nominal<"FormSchema"> &
{
readonly [K in keyof Values]: GenericFieldDescriptor<Values[K], Err>;
};

export type ExtractFormValues<Schema> = Schema extends FormSchema<infer V, any>
? V
: never;
5 changes: 2 additions & 3 deletions src/utils/utility-types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
class Tagged<T> {
export class Nominal<Tag> {
// @ts-ignore
private __tag!: T;
private __tag!: Tag;
}
export type Nominal<Tag, T> = Tagged<Tag> & T;

export type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never
Expand Down

0 comments on commit 6f4d16f

Please sign in to comment.