Dilswer
Small and lightweight data validation library with TypeScript integration.
Keep your type definitions in one place, and have but one source of truth for
both the runtime validation types and the TypeScript type definitions.
Quick Start
Create type definitions
import { DataType } from "dilswer";
export const PersonDataType = DataType.RecordOf({
id: { type: DataType.String, required: true },
name: { type: DataType.String },
age: { type: DataType.Number },
friends: { type: DataType.ArrayOf(DataType.String), required: false },
});
NOTE: the required
attribute in a RecordOf fields is set to true
by
default.
Create a TypeScript type from a Dilswer definition
import { GetDataType } from "dilswer";
import { PersonDataType } from "./person-type.ts";
type Person = GetDataType<typeof PersonDataType>;
Create a validation function
import { createChecker } from "dilswer";
import { PersonDataType } from "./person-type.ts";
const isPerson = createChecker(PersonDataType);
const person = await axios
.get("https://my-api.io/get-person/1")
.then((r) => r.data);
if (isPerson(person)) {
console.log("Name: ", person.name);
} else {
console.error("`person` variable is not of expected type.");
}
Create a function with a validated input
import { createChecker } from "dilswer";
import { PersonDataType } from "./person-type.ts";
const processPerson = createValidatedFunction(
PersonDataType,
(person) => {
console.log("Processing person: ", person.name);
return "Success!";
},
(error) => {
console.error("Function input is not of expected type.");
console.error("Type expected:", error.expectedValueType);
console.error("Received:", error.receivedValue);
console.error("Invalid property location: ", error.fieldPath);
return "Failure";
}
);
const person = await axios
.get("https://my-api.io/get-person/1")
.then((r) => r.data);
const result = processPerson(person);
Motivation
Whenever you use some kind of a type validation library in a TypeScript project
you will have to define those types twice: once as a TS type
or interface
and once in a format that's understood by the data validation library which will
check the data types on runtime. This is a inconvenience and can sometimes lead
to bugs (when you change one of the definitions but forget to do the same with
the other).
This is the problem that Dilswer is trying to solve. To have one source of
truth for your type definitions. One that can be understood by both the
TypeScript engine and the data validation library.
Dilswer gives you a tool that you can use to define any kind of type, and
then validate data at runtime with against it or infer a TypeScript type
directly from it.
Data Types
DataType.String
will match any string values and translate to the standard string
type in
TypeScript.
DataType.Number
will match any number values and translate to the standard number
type in
TypeScript.
DataType.Boolean
will match any true
and false
values and translate to the standard boolean
type in TypeScript.
DataType.Symbol
will match any symbolic values and translate to the symbol
type in TypeScript.
DataType.Null
will match only null
value and translate to the standard null
type in
TypeScript.
DataType.Undefined
will match only undefined
value and translate to the standard undefined
type
in TypeScript.
DataType.Function
will match any function and translate to the Function
type in TypeScript.
DataType.Unknown
will match any value and translate to the unknown
type in TypeScript.
DataType.OneOf(...DataType's)
will match any value matching one of the DataType's provided in the arguments
and translate to an TypeScript union type.
Example
const foo = DataType.OneOf(DataType.String, DataType.Number);
type T = GetDataType<typeof foo>;
DataType.ArrayOf(...DataType's)
will match any array which contains only values matching any of the DataType's
provided in the arguments and translate to the Array<...>
type in TypeScript.
Example
const foo = DataType.ArrayOf(DataType.String, DataType.Number);
type T = GetDataType<typeof foo>;
DataType.RecordOf(Record<string, FieldDescriptor>)
will match any object which structure matches the key-value pairs of object
properties and FieldDescriptor objects passed to the argument.
Example
const foo = DataType.RecordOf({
bar: { type: DataType.String },
baz: { type: DataType.Number, required: false },
});
type T = GetDataType<typeof foo>;
DataType.SetOf(...DataType's)
will match any Set object which contains only values matching any of the
DataType's provided in the arguments and translate to the Set<...>
type in
TypeScript.
Example
const foo = DataType.SetOf(DataType.String, DataType.Number);
type T = GetDataType<typeof foo>;
DataType.Literal(string | number | boolean)
will match any value that exactly matches the passed argument and translate to
the literal type of that value in TypeScript.
Example's
const foo = DataType.Literal("some-string-literal");
type T0 = GetDataType<typeof foo>;
const bar = DataType.Literal(123);
type T1 = GetDataType<typeof bar>;
const baz = DataType.Literal(true);
type T2 = GetDataType<typeof baz>;
will match any value that belongs to an TypeScript enum and translate to that
enum type.
enum MyEnum {
A = "A",
B = "B",
}
const foo = DataType.Enum(MyEnum);
type T = GetDataType<typeof foo>;
const validate = createChecker(foo);
validate(MyEnum.A);
DataType.EnumMember(enum member)
will match any value that equals to the specified TypeScript enum member and
translate to that enum member type.
enum MyEnum {
A = "VALUE_A",
B = "VALUE_B",
}
const foo = DataType.Enum(MyEnum.A);
type T = GetDataType<typeof foo>;
const validate = createChecker(foo);
validate("VALUE_A");
validate(MyEnum.A);
validate(MyEnum.B);