Skip to content

Commit

Permalink
docs: Add TSDoc comments to all important members
Browse files Browse the repository at this point in the history
  • Loading branch information
davidkarolyi committed Mar 7, 2022
1 parent ca1220a commit 316c9d1
Show file tree
Hide file tree
Showing 3 changed files with 262 additions and 13 deletions.
75 changes: 75 additions & 0 deletions src/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,49 @@ import { MissingValueError, InvalidValueError } from "./errors";

export type GuardedType<G extends Guard<Schema>> = ValidatorType<G>;

/**
* @param schema - Can be a single `Validator`, an other `Guard`, or an object of these types.
*
* @example
* ```ts
* const TPost = new Guard({
* id: TStringUUID({version: 4});
* title: TString,
* body: TString,
* });
*
* const TUser = new Guard({
* id: TStringUUID({version: 4});
* name: TString,
* posts: TArray(TPost),
* });
*
* // Note: If you don't want to define these types twice
* // (once as a TypeScript type, once as a guard):
* type User = GuardedType<typeof TUser>;
* type Post = GuardedType<typeof TPost>;
*
* // We have an unknown value, that we fetched from an external API,
* // TypeScript will implicitly infer it as "any" type:
* const john: any = {
* name: "John",
* posts: ["Who am I?", "I am a user."],
* };
*
* // Validate if John is a valid 'User' type or not:
* if (TUser.isValid(john)) {
* // TypeScript will infer John's type as 'User' in this block.
* }
*
* // Or try to cast a value to the User type:
* try {
* const user = TUser.cast({ posts: ["Who am I?", "I am a user."] });
* // Type of user is User
* } catch (error) {
* // error.message === 'Validation failed: Missing value at "id", expected type: string(UUID-v4)'
* }
* ```
*/
export class Guard<S extends Schema> extends Validator<SchemaType<S>> {
readonly name;
readonly schema: Tree<Validator<unknown>>;
Expand All @@ -30,6 +73,12 @@ export class Guard<S extends Schema> extends Validator<SchemaType<S>> {
return this.schema.map((validator) => validator.name).value;
}

/**
* The `isValid` method is a
* [type predicate function](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates).
*
* @returns If the the given `value` is matching the `schema`.
*/
isValid(value: any): value is SchemaType<S> {
try {
this.cast(value);
Expand All @@ -39,6 +88,32 @@ export class Guard<S extends Schema> extends Validator<SchemaType<S>> {
}
}

/**
* The `cast` method will take any value and return the same value, but typed as the guarded type.
* If the value isn't matching the schema, it will throw an `Error` containing the reason of failure.
*
* @example
* ```ts
* const TPost = new Guard({
* id: TStringUUID({version: 4});
* title: TString,
* body: TString,
* });
*
* const TUser = new Guard({
* id: TStringUUID({version: 4});
* name: TString,
* posts: TArray(TPost),
* });
*
* try {
* const user = TUser.cast({ posts: ["Who am I?", "I am a user."] });
* // Type of user is User
* } catch (error) {
* // error.message === 'Validation failed: Missing value at "id", expected type: string(UUID-v4)'
* }
* ```
*/
cast(value: any): SchemaType<S> {
if (this.schema.isLeafNode(this.schema.value)) {
if (!this.schema.value.isValid(value))
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { TreeDefinition } from "./tree";

/**
* An abstract class, which has an `isValid` method, and a `name` property, which represents the name of the guarded type.
*
* ⚠️ Don't use this directly to create custom validators, use `TValidate` instead.
*/
export abstract class Validator<T> {
abstract readonly name: string;
abstract isValid(value: any): value is T;
Expand Down
Loading

0 comments on commit 316c9d1

Please sign in to comment.