Skip to content

Commit

Permalink
feat: rework the remaining assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
yamiteru committed Apr 29, 2024
1 parent 4508e58 commit da45238
Show file tree
Hide file tree
Showing 19 changed files with 108 additions and 43 deletions.
4 changes: 2 additions & 2 deletions src/assertions/array/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import type { Validate } from "./types";
* ```
*/
export const array = <$Brand extends AnyBrand>(brand: $Brand) =>
((v: unknown) => {
isArray(v);
((v: any) => {
(isArray as any)(v);

for (let i = 0; i < v.length; ++i) {
(brand as any)((v as any)[i]);
Expand Down
2 changes: 1 addition & 1 deletion src/assertions/boolean/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import type { Validate } from "./types";
* boolean(true); // passes
* ```
*/
export const boolean: Validate.Type.Boolean = type("boolean");
export const boolean = type("boolean") as Validate.Type.Boolean;
2 changes: 1 addition & 1 deletion src/assertions/email/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import type { Validate } from "./types";
* email("[email protected]"); // passes
* ```
*/
export const email: Validate.Regex.Email = regex(/^\w+@.+\..+$/);
export const email = regex(/^\w+@.+\..+$/) as Validate.Regex.Email;
19 changes: 11 additions & 8 deletions src/assertions/expect/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { Message } from "@assertions/expect/types";
import type { Assertion } from "@the-minimal/types";
import { error } from "@error";
import type { AnyBrand } from "@the-minimal/types";

/**
* Wraps assertion and throws an error with message if assertion fails.
*
* @param assertion - Assertion to be checked.
* @param brand - Assertion to be checked.
* @param message - Message to be used in error.
*
* @example
Expand All @@ -15,12 +16,14 @@ import type { Assertion } from "@the-minimal/types";
* );
* ```
*/
export const expect =
<$Input>(assertion: Assertion<$Input>, message: Message): Assertion<$Input> =>
(v) => {
export const expect = <$Brand extends AnyBrand>(
brand: $Brand,
message: Message,
) =>
((v: unknown) => {
try {
assertion(v);
(brand as any)(v);
} catch (e: any) {
throw Error(message(e, v));
error(brand, message(e, v));
}
};
}) as unknown as $Brand;
6 changes: 3 additions & 3 deletions src/assertions/isArray/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { error } from "@error";
import type { Assertion } from "@the-minimal/types";
import type { Validate } from "./types";

/**
* Checks that the value is an array.
Expand All @@ -10,5 +10,5 @@ import type { Assertion } from "@the-minimal/types";
* isArray([]); // passes
* ```
*/
export const isArray: Assertion<Array<unknown>> = ((v) =>
Array.isArray(v) || error(isArray)) as Assertion<Array<unknown>>;
export const isArray = ((v: unknown) =>
Array.isArray(v) || error(isArray)) as unknown as Validate.Type.Array;
14 changes: 14 additions & 0 deletions src/assertions/isArray/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Brand, None } from "@the-minimal/types";

export namespace Validate {
export namespace Type {
export type Array = Brand<
"array",
None,
{
input: unknown[];
output: None;
}
>;
}
}
12 changes: 8 additions & 4 deletions src/assertions/isObject/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { ObjectUnknown } from "@assertions/isObject/types";
import type { Validate as AndValidate } from "@assertions/and/types";
// TODO: this is fucking ugly
import type { Validate as IsObjectValidate } from "@assertions/isObject/types";
import type { Validate as NotValueValidate } from "@assertions/notValue/types";
import { error } from "@error";
import type { Assertion } from "@the-minimal/types";

/**
* Checks that the value is of type object and is not null.
Expand All @@ -12,6 +14,8 @@ import type { Assertion } from "@the-minimal/types";
* isObject({}); // passes
* ```
*/
export const isObject: Assertion<ObjectUnknown> = ((v) =>
export const isObject = ((v: unknown) =>
(v !== null && typeof v === "object") ||
error(isObject)) as Assertion<ObjectUnknown>;
error(isObject)) as unknown as AndValidate.And<
[NotValueValidate.Value.Ne<null>, IsObjectValidate.Type.Object]
>;
15 changes: 15 additions & 0 deletions src/assertions/isObject/types.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
import type { Brand, None } from "@the-minimal/types";

export namespace Validate {
export namespace Type {
export type Object = Brand<
"object",
None,
{
input: ObjectUnknown;
output: None;
}
>;
}
}

export type ObjectUnknown = Record<string, unknown>;
4 changes: 2 additions & 2 deletions src/assertions/nullable/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AnyBrand } from "@the-minimal/types";
import type { General } from "./types";
import type { Validate } from "./types";

/**
* Checks if the assertion passes or if the value is null.
Expand All @@ -17,4 +17,4 @@ import type { General } from "./types";
*/
export const nullable = <$Brand extends AnyBrand>(brand: $Brand) =>
((v: unknown) =>
v === null || (brand as any)(v)) as unknown as General.Nullable<$Brand>;
v === null || (brand as any)(v)) as unknown as Validate.Nullable<$Brand>;
2 changes: 1 addition & 1 deletion src/assertions/number/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import type { Validate } from "./types";
* number(1); // passes
* ```
*/
export const number: Validate.Type.Number = type("number");
export const number = type("number") as Validate.Type.Number;
4 changes: 2 additions & 2 deletions src/assertions/object/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import type { General, ObjectSchema } from "@assertions/object/types";
export const object = <$Schema extends ObjectSchema>(schema: $Schema) => {
const keys = Object.keys(schema);

return ((v: unknown) => {
isObject(v);
return ((v: any) => {
(isObject as any)(v);

for (let i = 0; i < keys.length; ++i) {
(schema[keys[i]] as any)(v[keys[i] as any]);
Expand Down
4 changes: 2 additions & 2 deletions src/assertions/optional/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AnyBrand } from "@the-minimal/types";
import type { General } from "./types";
import type { Validate } from "./types";

/**
* Checks if the assertion passes or if the value is undefined.
Expand All @@ -18,4 +18,4 @@ import type { General } from "./types";
export const optional = <$Brand extends AnyBrand>(brand: $Brand) =>
((v: unknown) =>
v === undefined ||
(brand as any)(v)) as unknown as General.Optional<$Brand>;
(brand as any)(v)) as unknown as Validate.Optional<$Brand>;
4 changes: 2 additions & 2 deletions src/assertions/regex/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { error } from "@error";
import type { Validation } from "./types";
import type { Validate } from "./types";

/**
* Checks if value matches regex pattern.
Expand All @@ -16,4 +16,4 @@ import type { Validation } from "./types";
*/
export const regex = (pattern: RegExp) =>
((v: string) =>
pattern.test(v) || error(regex)) as unknown as Validation.Regex.Any;
pattern.test(v) || error(regex)) as unknown as Validate.Regex.Any;
2 changes: 1 addition & 1 deletion src/assertions/string/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import type { Validate } from "./types";
* string("hello"); // passes
* ```
*/
export const string: Validate.Type.String = type("string");
export const string = type("string") as Validate.Type.String;
4 changes: 2 additions & 2 deletions src/assertions/tuple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import type { TupleSchema, Validate } from "./types";
* ```
*/
export const tuple = <const $Schema extends TupleSchema>(assertions: $Schema) =>
((v: unknown) => {
isArray(v);
((v: any) => {
(isArray as any)(v);

for (let i = 0; i < assertions.length; ++i) {
(assertions[i] as any)(v[i]);
Expand Down
5 changes: 3 additions & 2 deletions src/assertions/type/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { error } from "@error";
import type { Type } from "./types";
import type { Validate } from "./types";

/**
* Checks that the value is of the provided type.
Expand All @@ -15,4 +15,5 @@ import type { Type } from "./types";
* ```
*/
export const type = (input: string) =>
((v: unknown) => typeof v === input || error(type)) as unknown as Type.Any;
((v: unknown) =>
typeof v === input || error(type)) as unknown as Validate.Type.Any;
17 changes: 11 additions & 6 deletions src/assertions/type/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type {Brand} from "@the-minimal/types";
import type { Brand } from "@the-minimal/types";

export namespace Validate {
export namespace Type {
export type Any = Brand<"type", any, {
input: any;
output: any;
}>;
export namespace Type {
export type Any = Brand<
"type",
any,
{
input: any;
output: any;
}
>;
}
}
8 changes: 6 additions & 2 deletions src/error.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const error = (assertion: unknown) => {
throw assertion;
export const error = (cause: unknown, message = "") => {
throw {
name: "Validation",
message,
cause,
};
};
23 changes: 21 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import type { AnyBrand } from "@the-minimal/types";
import type { InferInput } from "@types";
import type { InferOutput } from "@types";

export function assert<$Brand extends AnyBrand>(
brand: $Brand,
value: unknown,
): asserts value is InferInput<$Brand> {
): asserts value is InferOutput<$Brand> {
(brand as any)(value);
}

export function parse<$Brand extends AnyBrand>(brand: $Brand, value: unknown) {
(brand as any)(value);

return value as InferOutput<$Brand>;
}

export function is<$Brand extends AnyBrand>(
brand: $Brand,
value: unknown,
): value is InferOutput<$Brand> {
try {
(brand as any)(value);

return true;
} catch {
return false;
}
}

0 comments on commit da45238

Please sign in to comment.