farrow-schema
Powerful and extensible schema builder library. Create Schema like type of TypeSccript.
Installation
- npm
- Yarn
npm install farrow-schema
yarn add farrow-schema
Usage
import { Struct, ID, TypeOf } from "farrow-schema";
import { Validator } from "farrow-schema/validator";
const user = Struct({
id: ID,
name: String,
});
const result = Validator.validate(User, {
id: "foo",
name: "foo name",
});
if (result.isOk) {
console.log(result.value);
}
type User = TypeOf<typeof User>;
// 相同类型
type User = {
id: string;
name: string;
};
API
Schema Builder
Primitive
- Usage
const V = Number;
Schema | Type | Validation |
---|---|---|
const V = Number | type V = number | typeof V === 'number' && !isNaN(V) |
const V = String | type V = string | typeof V === 'string' |
const V = Boolean | type V = boolean | typeof V === 'boolean' |
const V = Date | type V = Date | Date.isDate(V) |
const V = ID | type V = number | typeof V === 'string' |
const V = Int | type V = number | Number.isInteger(V) |
const V = Float | type V = number | typeof V === 'number' && !isNaN(V) |
Literal
- Usage
const V = Literal(Number Value | String Value | Boolean Value | Date Value);
Schema | Type | Validation |
---|---|---|
const V = Literal(0) | type V = 0 | V === 0 |
const V = Literal("foo") | type V = "foo" | V === "foo" |
const V = Literal(true) | type V = true | V === true |
const V = Literal(C = new Date()) | type V = Date | V === C |
List
- Usage
const V = List($Schema);
- Example
// Schema
const V = List(Number);
// Type
type V = Array<number>;
// Validation
Array.isArray(V) && V.every((c) => typeof c === "number" && !isNaN(c));
// Schema
const V = List(Literal(0));
// Type
type V = Array<0>;
// Validation
Array.isArray(V) && V.every((c) => c === 0);
// Schema
const V = List(
Struct({
id: ID,
name: String,
})
);
// Type
type V = Array<{
id: string;
name: string;
}>;
// Validation
Array.isArray(V) &&
V.every((c) => {
const cIsObject = typeof c === "object";
const idIsString = typeof c.id === "string";
const nameIsString = typeof c.name === "string";
return cIsObject && idIsString && nameIsString;
});
Struct
- Usage
const FieldSchema = {
[key: string]: $Schema | $FieldSchema
}
const V = Struct($FieldSchema);
- Example
// Schema
const V = Struct({
id: ID,
name: String,
});
// Type
type V = {
id: string;
name: string;
};
// Validation
const cIsObject = typeof V === "object";
const idIsString = typeof V.id === "string";
const nameIsString = typeof V.name === "string";
cIsObject && idIsString && nameIsString;
// Schema
const V = Struct({
id: ID,
name: String,
artile: {
title: String,
description: String,
},
});
// Type
type V = {
id: string;
name: string;
article: {
title: string;
description: string;
};
};
// Validation
const cIsObject = typeof V === "object";
const idIsString = typeof V.id === "string";
const nameIsString = typeof V.name === "string";
const articleIsObject = typeof V.article === "object";
const titleIsString = typeof V.article.title === "string";
const descriptionIsString = typeof V.article.description === "string";
cIsObject &&
idIsString &&
nameIsString &&
articleIsObject &&
titleIsString &&
descriptionIsString;
caution
Struct
cannot construct recusive type. It will throw error if you make recursive type with this.
ObjectType
- Usage
class V extends ObjectType {
$FieldSchema;
...
}
- Example
// Schema
class V extends ObjectType {
id = ID;
name = String;
}
// Type
type V = {
id: string;
name: string;
};
// Validation
const vIsObject = typeof V === "object";
const idIsString = typeof V.id === "string";
const nameIsString = typeof V.name === "string";
vIsObject && idIsString && nameIsString;
// Schema
class V extends ObjectType {
value = Number;
next = Nullable(V);
}
// Type
type V = {
value: number;
next: V | undefined;
};
// Validation
const vIsObject = typeof V === "object";
const vauleIsNumber = typeof V.id === "number";
const nextIsVOrUndefined = V.name === undefined || V.next is V;
vIsObject && vauleIsNumber && nextIsVOrUndefined;
info
ObjectType
can construct recursive type.
note
One point to note about recursive type is that the constructed schema cannot be infinitely recursive, and there are three main ways to interrupt infinite recursion: option, lazy, and reference, while in farrow-schema, it is mainly by way of option. Nullable, List can do it.
Meta
const V = Null;
Schema | Type | Validation |
---|---|---|
const V = Null | type V = null | V === null |
const V = Undefined | type V = undefined | V === undefined |
const V = Any | type V = any | true |
const V = Unknown | type V = Unknown | true |
const V = Json | type V = JsonType | JSON.parse(JSON.stringify(V)) |
Generic
Union
const V = Union($Schema, $Schema);
Intersect
const V = Intersect($Schema, $Schema);
Tuple
const V = Tuple(...$Schema[]);
Record
const V = Record($Schema);
Nullable
const V = Nullable($Schema);
Strict
const V = Strict($Schema);
NonStrict
const V = NonStrict($Schema);
ReadOnly
const V = ReadOnly($Schema);
ReadOnlyDeep
const V = ReadOnlyDeep($Schema);
Declaration
const Foo = Struct({ foo: String });
const Bar = Struct({ bar: Number });
const Baz = Struct({ foo: { bar: String } });
Schema | Type | Validation |
---|---|---|
const V = Union(String, Number) | type V = string \| number | V is string \|\| V is number |
const V = Intersect(Foo, Bar) | type V = { foo: string } && { bar: number } | |
const V = Tuple(String, Number, Literal(0)) | type V = [string, number, 0] | Array.isArray(V) && typeof V[0] === "string" && typeof V[1] === "number" && V[2] === 0 |
const V = Record(Number) | type V = Record<string, number> | |
const V = Strict(Foo) | type V = { foo: string } | |
const V = NonStrict(Foo) | type V = { foo: string } | |
const V = ReadOnly(Foo) | type V = { readonly foo: string } | |
const V = ReadOnly(Baz) | type V = { readonly foo: { bar: string } } | |
const V = ReadOnlyDeep(Baz) | type V = { readonly foo: { readonly bar: string } } |
Util
pick
const V = pick(C = $Struct | $ObjectType, $keyof C);
omit
const V = omit(C = $Struct | $ObjectType, $keyof C);
keyof
const V = keyof($Struct | $ObjectType);
partial
const V = partial($Struct | $ObjectType);
Declaration
const Foo = Struct({ foo0: String, foo1: Number });
const Bar = Struct({ bar0: String, bar1: Number });
Schema | Type | Validation |
---|---|---|
const V = pick(Foo, "foo0") | type V = { foo0: string } | |
const V = pick(Bar, "bar0") | type V = { bar0: string } | |
const V = omit(Foo, "foo0") | type V = { foo1: number } | |
const V = omit(Bar, "bar0") | type V = { bar1: number } | |
const V = keyof(Foo) | type V = "foo0" \| "foo1" | |
const V = keyof(Bar) | type V = "bar0" \| "bar1" | |
const V = partial(Foo) | type V = { foo0?: string, foo1?: number } | |
const V = partial(Bar) | type V = { bar0?: string, bar1?: number } |
Formatter
formatSchema
Type Signature:
const formatSchema: <T extends S.SchemaCtor<S.Schema>>(
Ctor: T,
context?: FormatContext | undefined
) => {
typeId: number;
types: FormatTypes;
};
type FormatContext = {
addType: (type: FormatType) => number;
formatCache: WeakMap<Function, number>;
};
Example Usage:
const result = formatSchema(Number)
// alternative
{
typeId: 0,
types: {
'0': {
type: 'Scalar',
valueType: 'number',
valueName: 'Number',
},
},
}
Validator
createSchemaValidator
Type Signature:
const createSchemaValidator: <S extends S.SchemaCtor<S.Schema>>(
SchemaCtor: S,
options?: ValidatorOptions | undefined
) => (input: unknown) => ValidationResult<S.TypeOf<S>>;
Example Usage:
import { ID, Struct } from "farrow-schema";
import { Validator } from "farrow-schema/validator";
const User = Struct({
id: ID,
name: String,
});
const result = Validator.validate(User, {
id: "foo",
name: "foo name",
});
if (result.isOk) {
console.log(result.value);
}
Strict Mode
The strict mode is defaultly open while validating. When a Schema wrapped by NonStrict
, the strict mode will be closed. It affects below schema.
tip
You can control the strict mode by option of Validator.validate
, like:
const result1 = Validator.validate(Number, "123.32", { strict: false });
- Number
const result0 = Validator.validate(Number, 123.32);
The result0
always is 123.32
(It means validate successfully).
const result1 = Validator.validate(Number, "123.32");
const result1 = Validator.validate(Number, "123.32", { strict: false });
But the result1
will be false
when strict mode is open and 123.32
when strict mode is close. It will try to parse string
to number
.
- Int
const result0 = Validator.validate(Int, 123);
The result0
always is 123
.
const result1 = Validator.validate(Int, 123.32);
const result1 = Validator.validate(Int, 123.32, { strict: false });
But the result1
will be false
when strict mode is open and 123
when strict mode is close. It will try to Math.floor
the input.
const result2 = Validator.validate(Int, "123");
const result2 = Validator.validate(Int, "123", { strict: false });
But the result2
will be false
when strict mode is open and 123
when strict mode is close. It will try to parse string
to number
.
const result3 = Validator.validate(Int, "123.32");
const result3 = Validator.validate(Int, "123.32", { strict: false });
But the result3
will be false
when strict mode is open and 123
when strict mode is close. It will try to parse string
to number
and Math.floor
the result.
- Float
const result0 = Validator.validate(Float, 123.32);
The result0
always is true
.
const result1 = Validator.validate(Float, "123.32");
const result1 = Validator.validate(Float, "123.32", { strict: false });
But the result1
will be false
when strict mode is open and 123.32
when strict mode is close. It will try to parse string
to number
.
- Boolean
const result0 = Validator.validate(Boolean, true);
The result0
always is true
.
const result1 = Validator.validate(Boolean, "true");
const result1 = Validator.validate(Boolean, "true", { strict: false });
But the result1
will be false
when strict mode is open and true
when strict mode is close. It will try to parse string
to boolean
.
- Literal
const result0 = Validator.validate(Literal(123), 123);
The result0
always is true
.
const result1 = Validator.validate(Literal(123), "123");
const result1 = Validator.validate(Literal(123), "123", { strict: false });
But the result1
will be false
when strict mode is open and 123
when strict mode is close. It will try to parse string
to boolean
.
- Struct
Schema declaration
const Foo = Struct({
id: Number,
name: String,
});
const result0 = Validator.validate(Foo, { id: 0, name: "foo" });
The result0
always is true
.
const result1 = Validator.validate(Literal(123), '{ id: 0, name: "foo" }');
const result1 = Validator.validate(Literal(123), '{ id: 0, name: "foo" }', {
strict: false,
});
But the result1
will be false
when strict mode is open and { id: 0, name: "foo" }
when strict mode is close. It will try to parse string
to JsonType
.
Type Infer
TypeOf
- Usage
type T = TypeOf<typeof $Schema>
- Example
type T = TypeOf<typeof Number>;
// alternative
type T = number;
type T = TypeOf<typeof List(Number)>;
// alternative
type T = number[];
type T = TypeOf<typeof Union(Number, String)>;
// alternative
type T = number | string;
type T = TypeOf<typeof Struct({ foo: String })>;
// alternative
type T = { foo: string };
Learn more
Relative Module
- farrow-api: Schema-based Api builder.
- farrow-api-server: farrow-api adapter for farrow-http.
- farrow-http: A Type-Friendly Web Framework based on farrow-pipeline.
- farrow-json-schema: Tool for Transforming farrow-schema to json schema.
Sample
- farrow-sample/03-schema-base: Base usage of farrow-pipeline.
- farrow-sample/04-schema-more: More compelex sample of farrow-pipeline.