Skip to content

Commit

Permalink
Make the path argument optional
Browse files Browse the repository at this point in the history
  • Loading branch information
kshramt committed Aug 16, 2023
1 parent 7ea14cb commit f75b694
Showing 1 changed file with 75 additions and 34 deletions.
109 changes: 75 additions & 34 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type TTags = {
readonly?: true;
};
type TValidator<T> = {
(value: unknown, path: TRef<TPath>): value is T;
(value: unknown, path?: TRef<TPath>): value is T;
[TAGS]?: TTags;
};
export type $infer<VT> = VT extends TValidator<infer T> ? T : never;
Expand All @@ -47,15 +47,15 @@ export type TOpaque<Name extends string, T> = T & {
};

export const $readonly = <V extends TValidator<unknown>>(validator: V) => {
const res = ((value: unknown, path: TRef<TPath>): value is $infer<V> => {
const res = ((value: unknown, path?: TRef<TPath>): value is $infer<V> => {
return validator(value, path);
}) as V & { [TAGS]: { readonly: true } };
res[TAGS] = { ...validator[TAGS], readonly: true };
return res;
};

export const $optional = <V extends TValidator<unknown>>(validator: V) => {
const res = ((value: unknown, path: TRef<TPath>): value is $infer<V> => {
const res = ((value: unknown, path?: TRef<TPath>): value is $infer<V> => {
return validator(value, path);
}) as V & { [TAGS]: { optional: true } };
res[TAGS] = { ...validator[TAGS], optional: true };
Expand All @@ -66,34 +66,40 @@ export const $opaque = <Name extends string, T>(
_: Name,
validator: TValidator<T>,
) => {
return (value: unknown, path: TRef<TPath>): value is TOpaque<Name, T> => {
return (value: unknown, path?: TRef<TPath>): value is TOpaque<Name, T> => {
return validator(value, path);
};
};

export const $typeGuard = <T>(
validator: (x: unknown, ...rest: unknown[]) => x is T,
) => {
return (value: unknown, path: TRef<TPath>): value is T => {
return (value: unknown, path?: TRef<TPath>): value is T => {
if (validator(value)) {
return true;
}
path.value = { invalid_value: value };
if (path) {
path.value = { invalid_value: value };
}
return false;
};
};

export const $tuple = <T extends unknown[]>(
...validators: [...{ [K in keyof T]: TValidator<T[K]> }]
) => {
return (value: unknown, path: TRef<TPath>): value is T => {
return (value: unknown, path?: TRef<TPath>): value is T => {
if (!Array.isArray(value)) {
path.value = { not_array: value };
if (path) {
path.value = { not_array: value };
}
return false;
}
for (let i = 0; i < validators.length; ++i) {
if (!validators[i](value[i], path)) {
path.value = { invalid_element_value: { key: i, path: path.value } };
if (path) {
path.value = { invalid_element_value: { key: i, path: path.value } };
}
return false;
}
}
Expand All @@ -111,24 +117,30 @@ type TNarrowable =
| undefined;

export const $literal = <T extends TNarrowable>(val: T) => {
return (value: unknown, path: TRef<TPath>): value is T => {
return (value: unknown, path?: TRef<TPath>): value is T => {
if (value === val) {
return true;
}
path.value = { invalid_value: value };
if (path) {
path.value = { invalid_value: value };
}
return false;
};
};

export const $array = <T>(validator: TValidator<T>) => {
return (value: unknown, path: TRef<TPath>): value is T[] => {
return (value: unknown, path?: TRef<TPath>): value is T[] => {
if (!Array.isArray(value)) {
path.value = { not_array: value };
if (path) {
path.value = { not_array: value };
}
return false;
}
for (let i = 0; i < value.length; ++i) {
if (!validator(value[i], path)) {
path.value = { invalid_element_value: { key: i, path: path.value } };
if (path) {
path.value = { invalid_element_value: { key: i, path: path.value } };
}
return false;
}
}
Expand All @@ -139,7 +151,7 @@ export const $array = <T>(validator: TValidator<T>) => {
export const $union = <T extends unknown[]>(
...validators: [...{ [K in keyof T]: TValidator<T[K]> }]
) => {
return (value: unknown, path: TRef<TPath>): value is T[number] => {
return (value: unknown, path?: TRef<TPath>): value is T[number] => {
const paths = [];
for (const validator of validators) {
const p: TRef<TPath> = {};
Expand All @@ -149,7 +161,9 @@ export const $union = <T extends unknown[]>(
paths.push(p.value);
}
}
path.value = { not_union: paths };
if (path) {
path.value = { not_union: paths };
}
return false;
};
};
Expand Down Expand Up @@ -204,22 +218,28 @@ export const $record = <K extends string, VV extends TValidator<unknown>>(
) => {
return (
value: unknown,
path: TRef<TPath>,
path?: TRef<TPath>,
): value is VV extends { [TAGS]: { readonly: true } }
? { readonly [_ in K]: $infer<VV> }
: { [_ in K]: $infer<VV> } => {
if (!isObject(value)) {
path.value = { not_object: value };
if (path) {
path.value = { not_object: value };
}
return false;
}
for (const k in value) {
if (!vk(k, path)) {
path.value = { invalid_element_key: { path: path.value } };
if (path) {
path.value = { invalid_element_key: { path: path.value } };
}
return false;
}
const v = value[k];
if (!vv(v, path)) {
path.value = { invalid_element_value: { key: k, path: path.value } };
if (path) {
path.value = { invalid_element_value: { key: k, path: path.value } };
}
return false;
}
}
Expand All @@ -230,9 +250,11 @@ export const $record = <K extends string, VV extends TValidator<unknown>>(
export const $object = <Kvs extends Record<string, TValidator<unknown>>>(
kvs: Kvs,
) => {
return (value: unknown, path: TRef<TPath>): value is TInferValues<Kvs> => {
return (value: unknown, path?: TRef<TPath>): value is TInferValues<Kvs> => {
if (!isObject(value)) {
path.value = { not_object: value };
if (path) {
path.value = { not_object: value };
}
return false;
}
for (const k in kvs) {
Expand All @@ -241,12 +263,18 @@ export const $object = <Kvs extends Record<string, TValidator<unknown>>>(
if (validator[TAGS]?.optional) {
continue;
}
path.value = { not_found: k };
if (path) {
path.value = { not_found: k };
}
return false;
} else {
const result = validator(value[k], path);
if (!result) {
path.value = { invalid_element_value: { key: k, path: path.value } };
if (path) {
path.value = {
invalid_element_value: { key: k, path: path.value },
};
}
return false;
}
}
Expand All @@ -255,55 +283,68 @@ export const $object = <Kvs extends Record<string, TValidator<unknown>>>(
};
};

const _$number = (value: unknown, path: TRef<TPath>): value is number => {
const _$number = (value: unknown, path?: TRef<TPath>): value is number => {
if (typeof value === "number") {
return true;
}
path.value = { not_number: value };
if (path) {
path.value = { not_number: value };
}
return false;
};
export const $number = () => {
return _$number;
};

const _$string = (value: unknown, path: TRef<TPath>): value is string => {
const _$string = (value: unknown, path?: TRef<TPath>): value is string => {
if (typeof value === "string") {
return true;
}
path.value = { not_string: value };
if (path) {
path.value = { not_string: value };
}
return false;
};
export const $string = () => {
return _$string;
};

const _$boolean = (value: unknown, path: TRef<TPath>): value is boolean => {
const _$boolean = (value: unknown, path?: TRef<TPath>): value is boolean => {
if (typeof value === "boolean") {
return true;
}
path.value = { not_boolean: value };
if (path) {
path.value = { not_boolean: value };
}
return false;
};
export const $boolean = () => {
return _$boolean;
};

const _$null = (value: unknown, path: TRef<TPath>): value is null => {
const _$null = (value: unknown, path?: TRef<TPath>): value is null => {
if (value === null) {
return true;
}
path.value = { not_null: value };
if (path) {
path.value = { not_null: value };
}
return false;
};
export const $null = () => {
return _$null;
};

const _$undefined = (value: unknown, path: TRef<TPath>): value is undefined => {
const _$undefined = (
value: unknown,
path?: TRef<TPath>,
): value is undefined => {
if (value === undefined) {
return true;
}
path.value = { not_undefined: value };
if (path) {
path.value = { not_undefined: value };
}
return false;
};
export const $undefined = () => {
Expand Down

0 comments on commit f75b694

Please sign in to comment.