Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add ExtractFormValues type #45

Merged
merged 1 commit into from
Nov 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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