diff --git a/docs/1-engine.md b/docs/1-engine.md new file mode 100644 index 0000000..fd26c44 --- /dev/null +++ b/docs/1-engine.md @@ -0,0 +1,144 @@ +# ts-cast · Type Checking Engine API + + +## 1. CasterFn<T> `type` + +The named function that accepts `unknown` type value and returns value of +target type `T` or reports about casting error: + +```ts +export interface CasterFn { + (value: unknown, context?: string, reportError?: ErrorReporter): T; + name: string; +} + +export type ErrorReporter = (message: string, context?: string) => never; +``` + +### Argumernts + +| Parameter | Type | Mandatory / Optional | Descriptions | +| :-------------: | :-------------: | :------------------: | :------------------------------------------------------------------------------------------------------------------------------------ | +| **value** | `unknown` | **mandatory** | The value to be casted | +| **context** | `string` | _optional_ | The Casting Context. It is could be considered as a name of root object to be casted. As instance it could be "SomeApiMethodResponse" | +| **reportError** | `ErrorReporter` | _optional_ | The function accepts an error message and casting context. It could throw an error or return undefined. | + +### Returns + - **casted value**: `T` + +### Example + +```ts +import { CasterFn, ErrorReporter } from 'ts-cast'; + +const casterFn: CasterFn = function yearOf21stCentury( + value: unknown, + context?: string, + reportError: ErrorReporter = (msg: string) => { throw new TypeError(msg); } +): number { + if (typeof value === 'number' && value > 2000 && value < 2101) { + return value; + } + + reportError( + `Current Year is expected${ + context ? ` in ${context}` : '' + } but "${value}" received.`, + context, + ); +} +``` + + +## 2. Caster<T> type + +The extenstion of the `CasterFn` that contains properties and methods to build other types, +including by mapping casted data. + +Please note that `CasterFn` doesn't have correspondent methods. + +### Type Definition + +```ts +export interface Caster extends CasterFn { + optional: Caster; + nullable: Caster; + default(defaltValue: T): Caster>; + restrict(...rules: RuleFn>[]): Caster; + map( + transform: (value: Exclude + ) => D): Caster>>; + validate(value: unknown): ValidationResult; + + either( + leftFactory: (error: TypeError) => Left, + rightFactory:(value: T) => Right, + ): CasterFn; + + validation( + invalidFactory: (errors: ErrorMessage[]) => Invalid, + validFactory: (value: T) => Valid + ): CasterFn; +} +``` + +### Property `.optional`: `Caster` + +The `.optional` property refers to `Caster` of **optional** type. Optional is considered as _could be undefined_. + +**Example** + +```ts +const optNumber = number.optional; + +optNumber(10); // returns 10 +optNumber(undefined); // returns undefined +optNumber(null); // throws a TypeError +``` + +### Property `.nullable`: `Caster` + +The `.nullable` property refers to `Caster` of **nullable** type that means the correspondent caster accepts `null` as valid value. + +**Example** + +```ts +const nullableStr = string.nullable; + +nullableStr('Hello World'); // returns "Hello World" +nullableStr(null); // returns null +nullableStr(undefined); // throws a TypeError +``` + +### Method `.default(value: T)` : `Caster>` + +Creates new `Caster` that replaces `undefined` input with `value` specified as argument. + +**Arguments** + +| Parameter | Type | Mandatory | Description | +| :-------: | :---: | :-------: | :----------------------------------- | +| **value** | `T` | **yes** | The value to replace `undefined` input | + +**Returns** + - **caster**: `Caster>` + +**Example** + +```ts +const someStr = string.default('no-input'); + +someStr('Hello World'); // returns "Hello World" +someStr(undefined); // returns "no-input" +someStr(null); // throws a TypeError +``` + + +## 3. casterApi<T> `fn` + + +## 4. createCaster<T> fn + + + +## 5. validate<T> fn \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..1aec586 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,69 @@ +# ts-cast · API Specification + +Runtime type checking for Typescript and JavaScript projects. + +## Refernce + +1. [Type Checking Engine](./1-engine.md) + 1. [CasterFn<T> `type`](./1-engine.md#1-caster-fn) + 2. [Caster<T> type](./1-engine.md#2-caster) + 3. [casterApi<T> `fn`](./1-engine.md#3-caster-api) + 4. [createCaster<T> fn](./1-engine.md#4-create-cater) + 5. [validate fn](./1-engine.md#5-validate) + +2. [Primitive Type Casters](./2-types.md#1-primitives) + 1. [boolean](./2-types.md$1-1-boolean) + 2. [integer](./2-types.md$1-2-integer) + 3. [number](./2-types.md$1-3-number) + 4. [string](./2-types.md$1-4-string) + 5. [text.integer](./2-types.md$1-5-text.integer) + 6. [text.number](./2-types.md$1-6-text.number) + +3. [Literal Types Casters](./2-types.md#2-literals) + 1. [value](./2-types.md#2-1-value) + 2. [values](./2-types.md#2-2-values) + +4. [Special Types Casters](./2-types.md#3-special) + 1. [any](./2-types.md#3-1-any) + 2. [nil](./2-types.md#3-2-nil) + 3. [undef](./2-types.md#3-3-undef) + +5. [Complex Type Casters](./2-types.md#4-complex) + 1. [array](./2-types.md#4-1-array) + 2. [record](./2-types.md#4-2-record) + 3. [struct](./2-types.md#4-3-struct) + 4. [tuple](./2-types.md#4-4-tuple) + +6. [Recursive Type Casters](./2-types.md) + +7. [Type Composition](./3-operations.md) + 1. [prod fn](./3-operations.md#1-prod) + 2. [union fn](./3-operations.md#2-union) + +8. [Narrowing Types](./4-restrictions.md#1-rules) + 1. [Predicate<T> type](./4-restrictions.md#1-1-predicate) + 2. [RuleFn<T> type](./4-restrictions.md#1-2-rulefn) + 3. [toBe fn](./4-restrictions.md#1-3-tobe) + 4. [notToBe fn](./4-restrictions.md#1-4-nottobe) + 5. [and fn](./4-restrictions.md#1-5-and) + 6. [or fn](./4-restrictions.md#1-6-or) + 7. [not fn](./4-restrictions.md#1-7-not) + +9. [Predicates](./4-restrictions.md#2-predicates) + 1. [greaterThen (gt)](./4-restrictions.md#2-1-gt) + 2. [lessThen (lt)](./4-restrictions.md#2-2-lt) + 3. [notGreaterThen (gte)](./4-restrictions.md#2-3-gte) + 4. [notLessThen (lte)](./4-restrictions.md#2-4-lte) + 5. [nonEmpty](./4-restrictions.md#2-5-notempty) + 6. [longerThen (length.gt)](./4-restrictions.md#2-6-length-gt) + 7. [shorterThen (length.lt)](./4-restrictions.md#2-7-length-lt) + 8. [notLongerThen (length.lte)](./4-restrictions.md#2-8-length-lte) + 9. [notShorterThen (length.gte)](./4-restrictions.md#2-9-length-gte) + 10. [matching](./4-restrictions.md#2-10-matching) + 11. [notEqual (ne)](./4-restrictions.md#2-11-ne) + +10. [Type Guards](./6-type-guards.md) + +## How To + 1. [Create Custom Types](./custom-types.md) + 2. [Cast to Either<\*, T> and Validation<\*, T>](./monadic-caster.md) \ No newline at end of file diff --git a/examples/advanced/src/index.ts b/examples/advanced/src/index.ts index 8511ec6..e173791 100644 --- a/examples/advanced/src/index.ts +++ b/examples/advanced/src/index.ts @@ -26,6 +26,6 @@ const myBook = Book({ const book: TBook = myBook; -me.id = UUID('123123123'); // throws TypeError in runtime +// me.id = UUID('123123123'); // throws TypeError in runtime console.log(JSON.stringify(book, null, 2)); \ No newline at end of file diff --git a/examples/advanced/src/schema.ts b/examples/advanced/src/schema.ts index e9e089e..9ee82ee 100644 --- a/examples/advanced/src/schema.ts +++ b/examples/advanced/src/schema.ts @@ -2,13 +2,17 @@ import v from 'validator'; import { integer, number, string, struct, tuple, array, union, nil, ref, createCaster, Caster, toBe } from 'ts-cast'; +import { CasterFn, ErrorReporter } from 'ts-cast/lib/engine/types'; class TEmail extends String { private tag: Symbol }; class TUUID extends String { private tag: Symbol }; -const isEmail = (value: unknown): value is TEmail => v.isEmail(value); -const isUUID = (value: unknown): value is TUUID => v.isUUID(value); +const isEmail = (value: unknown): value is TEmail => + typeof value === 'string' && v.isEmail(value); + +const isUUID = (value: unknown): value is TUUID => + typeof value === 'string' && v.isUUID(value); export const Email = createCaster('email', isEmail, (email): TEmail => email.toLowerCase()); export const UUID = createCaster('uuid', isUUID); @@ -43,4 +47,4 @@ export const Book = struct({ export type TCoords = ReturnType; -export type TBook = ReturnType; \ No newline at end of file +export type TBook = ReturnType;