Blazingly fast data validation library with TypeScript integration.
- Quick Start
- Available Types
- Utility Functions
- Metadata
- JSON Schema Generation
- TypeScript Type Generation
- Parsing
The fastest way to validate data is using the compile() function:
import { compile, Type } from "dilswer";
// Compile a validation function from a type definition
const isString = compile(Type.String);
isString("hello"); // true
isString(123); // falseFor complex types:
import { compile, Type } from "dilswer";
const PersonValidator = compile(
Type.Record({
id: Type.String,
name: Type.String,
age: Type.Number,
email: Type.Option(Type.String),
friends: Type.Option(Type.Array(Type.String)),
})
);
// Valid data
PersonValidator({
id: "abc123",
name: "Alice",
age: 30,
}); // true
// Invalid data
PersonValidator({
name: "Bob",
age: "not a number", // age should be a number
}); // falsePerformance note: Compiled validators are extremely fast but provide minimal error messages. For detailed error information, use the validator() function instead.
import { Type } from "dilswer";
// Define a record type with various field types
const PersonDataType = Type.Record({
id: Type.String,
name: Type.String,
age: Type.Number,
email: Type.Option(Type.String),
friends: Type.Option(Type.Array(Type.String)),
});
// The equivalent TypeScript type would be:
// {
// id: string;
// name: string;
// age: number;
// email?: string;
// friends?: string[];
// }import { Infer, compile, Type } from "dilswer";
const PersonDataType = Type.Record({
id: Type.String,
name: Type.String,
age: Type.Number,
});
type Person = Infer<typeof PersonDataType>;
// type Person = {
// id: string;
// name: string;
// age: number;
// }
// Works with compiled validators too
const PersonValidator = compile(PersonDataType);
// PersonValidator(v: any): v is {
// id: string;
// name: string;
// age: number;
// }If you need detailed information about validation failures, use validator() with the details option:
import { validator, Type } from "dilswer";
const isPerson = validator(PersonDataType, { details: true });
const result = isPerson({ name: "Alice" });
if (result.success) {
console.log(result.value.name);
} else {
console.error("Validation failed:", result.error.message);
// Access the path where validation failed:
console.error("Failed at:", result.error.fieldPath);
console.error("Details:", result.error.details());
console.error("DetailsObject:", JSON.stringify(result.error.detailsJson()));
}details() returns a string containing information about the error more detailed than just the error message.
ValidationError: not an array
Path: $.foo.bar
Expected: ArraySchema[ PrimitiveSchema[ string ] ]
Got: object
AggregateValidationError: does not match any of the types in the union
Path: $
Errors:
ValidationError: not an object
Path: $
Expected: RecordSchema[ type=LiteralSchema[ a ]; foo=PrimitiveSchema[ number ]; bar=PrimitiveSchema[ string ] ]
Got: object
ValidationError: not an object
Path: $
Expected: RecordSchema[ type=LiteralSchema[ b ]; foo=PrimitiveSchema[ string ]; baz=PrimitiveSchema[ string ] ]
Got: object
ValidationError: not an object
Path: $
Expected: RecordSchema[ type=LiteralSchema[ c ]; foo=PrimitiveSchema[ boolean ]; qux=PrimitiveSchema[ string ] ]
Got: object
A shorthand for validate with details.
import { validateWith, Type } from "dilswer";
const result = validateWith(Type.String, value);
if (result.success) {
console.log(result.value);
} else {
console.error("Validation failed:", result.error.message);
}The assertion function throws an error if validation fails, which is useful for early returns:
import { assertType, Type } from "dilswer";
function greet(value: unknown) {
try {
assertType(Type.String, value);
value.toUpperCase(); // value is now typed as string
} catch (error) {
console.error("Value is not a string");
}
}Dilswer schemas are compatible with libraries that support the Standard Schema specification, such as tRPC and OpenAPI.
import { initTRPC } from "@trpc/server";
import { compile, Type } from "dilswer";
const t = initTRPC.create();
// For maximum performance, compile schemas before passing them to procedures
const router = t.router({
greeting: t.procedure
.input(
Type.Record({
name: Type.String,
}).compile(),
)
.query(async ({ input }) => {
return `Hello, ${input.name}!`;
}),
});Uncompiled schemas also work but may have slightly lower performance:
const router = t.router({
greeting: t.procedure
.input(
Type.Record({
name: Type.String,
})
)
.query(async ({ input }) => {
return `Hello, ${input.name}!`;
}),
});Matches any string value.
const validator = compile(Type.String);
validator("hello"); // true
validator(123); // falseMatches any numeric value (including floats and integers).
const validator = compile(Type.Number);
validator(1.5); // true
validator(42); // true
validator("123"); // falseMatches only integer values.
const validator = compile(Type.Int);
validator(42); // true
validator(1.5); // falseMatches true and false values.
const validator = compile(Type.Boolean);
validator(true); // true
validator(false); // true
validator(1); // falseMatches symbol values.
const validator = compile(Type.Symbol);
validator(Symbol("test")); // true
validator("sym"); // falseMatches only the null value.
const validator = compile(Type.Null);
validator(null); // true
validator(undefined); // falseMatches only the undefined value.
const validator = compile(Type.Undefined);
validator(undefined); // true
validator(null); // falseMatches any function.
const validator = compile(Type.Function);
validator(() => {}); // true
validator(async () => {}); // true
validator("not a fn"); // falseMatches any value.
const validator = compile(Type.Unknown);
validator("anything"); // true
validator(123); // true
validator(null); // true// Matches strings containing only numeric characters
// Safe to convert to number without producing NaN
const numeralValidator = compile(Type.String.Float);
numeralValidator("123"); // true
numeralValidator("12.5"); // true (float strings match)
numeralValidator("abc"); // false
// Matches strings containing only integer characters
const intValidator = compile(Type.String.Int);
intValidator("123"); // true
intValidator("12.5"); // false
// Matches strings containing only positive integer characters
const positiveIntValidator = compile(Type.String.Int.positive());
positiveIntValidator("123"); // true
positiveIntValidator("-1"); // false
// Matches strings that conform to a regular expression
const hexValidator = compile(Type.String.matching(/^[0-9a-f]+$/i));
hexValidator("abc123"); // true
hexValidator("xyz"); // false
// Matches strings with length contraints
const lenValidator = compile(Type.String.len({ min: 2, max: 5 }));
lenValidator("abc"); // true
lenValidator("x"); // false
lenValidator("12345678"); // falseMatches objects with specified properties.
const PersonValidator = compile(
Type.Record({
name: Type.String,
age: Type.Number,
active: Type.Option(Type.Boolean),
})
);
PersonValidator({ name: "Alice", age: 30 }); // true
PersonValidator({ name: "Bob", age: 25, active: true }); // true
PersonValidator({ name: "Carol" }); // false (age is required)Matches arrays containing elements of specified types.
const ArrayValidator = compile(Type.Array(Type.String, Type.Number));
ArrayValidator(["a", "b", 1, 2]); // true
ArrayValidator(["a", "b", "c"]); // true
ArrayValidator([1, 2, 3]); // true
ArrayValidator("not an array"); // falseMatches arrays with a fixed number of elements of specific types.
const TupleValidator = compile(
Type.Tuple(Type.String, Type.Number, Type.Boolean)
);
TupleValidator(["hello", 42, true]); // true
TupleValidator(["hello", 42]); // false (wrong length)
TupleValidator(["hello", "world"]); // false (second element is not a number)Matches Set objects containing elements of specified types.
const SetValidator = compile(Type.Set(Type.String));
SetValidator(new Set(["a", "b", "c"])); // true
SetValidator(new Set([1, 2, 3])); // false
SetValidator(["a", "b"]); // false (not a Set)Matches objects where all values match the specified types.
const DictValidator = compile(Type.Dict(Type.String));
DictValidator({ a: "x", b: "y" }); // true
DictValidator({ a: 1, b: 2 }); // false
DictValidator("not an object"); // falseMatches values that match any one of the provided types (union).
const UnionValidator = compile(Type.OneOf(Type.String, Type.Number));
UnionValidator("hello"); // true
UnionValidator(123); // true
UnionValidator(true); // falseMatches values that match all of the provided types (intersection).
const A = Type.Record({ foo: Type.String });
const B = Type.Record({ bar: Type.Number });
const IntersectedValidator = compile(Type.AllOf(A, B));
IntersectedValidator({ foo: "hello", bar: 42 }); // true
IntersectedValidator({ foo: "hello" }); // false (missing bar)Matches exact values.
const TrueValidator = compile(Type.Literal(true));
const StrValidator = compile(Type.Literal("hello"));
const NumValidator = compile(Type.Literal(42));
TrueValidator(true); // true
TrueValidator(false); // false
StrValidator("hello"); // true
StrValidator("world"); // false
NumValidator(42); // true
NumValidator(100); // falseMatches TypeScript enum values.
enum MyEnum {
A = "A",
B = "B",
}
const EnumValidator = compile(Type.Enum(MyEnum));
EnumValidator(MyEnum.A); // true
EnumValidator(MyEnum.B); // true
EnumValidator("C"); // falseMatches a specific enum member.
enum MyEnum {
A = "VALUE_A",
B = "VALUE_B",
}
const MemberValidator = compile(Type.EnumMember(MyEnum.A));
MemberValidator(MyEnum.A); // true
MemberValidator("VALUE_A"); // true (string value matches)
MemberValidator(MyEnum.B); // falseMatches instances of a class.
class User {
constructor(public name: string) {}
}
const UserValidator = compile(Type.InstanceOf(User));
UserValidator(new User("Alice")); // true
UserValidator({ name: "Bob" }); // falseMatches values using a custom validation function. The function must be a type guard ((v: any) => v is T).
const NonEmptyString = Type.Custom(
(v: any): v is string => typeof v === "string" && v.length > 0
);
const NonEmptyValidator = compile(NonEmptyString);
NonEmptyValidator("hello"); // true
NonEmptyValidator(""); // false
NonEmptyValidator(123); // falseDefines types that reference themselves.
const Node = Type.Recursive(self =>
Type.Record({
tag: Type.String,
children: Type.Array(self),
})
);
const NodeValidator = compile(Node);
NodeValidator({
tag: "div",
children: [
{ tag: "span", children: [] },
{ tag: "p", children: [] },
],
}); // trueMakes a field optional (can be undefined).
const Validator = compile(
Type.Record({
name: Type.String,
email: Type.Option(Type.String),
})
);
Validator({ name: "Alice" }); // true
Validator({ name: "Bob", email: "bob@example.com" }); // true
Validator({ name: "Carol", email: undefined }); // true
Validator({ email: "dan@example.com" }); // false (name is required)Combines two Record types. Properties from the second type override conflicting properties from the first.
const TypeA = Type.Record({
foo: Type.String,
bar: Type.Number,
});
const TypeB = Type.Record({
bar: Type.Boolean,
baz: Type.String,
});
const Combined = compile(And(TypeA, TypeB));
// Validates: { foo: string, bar: boolean, baz: string }Removes specified keys from a Record type.
const Original = Type.Record({
foo: Type.String,
bar: Type.Number,
baz: Type.Boolean,
});
const Simplified = compile(Omit(Original, "bar", "baz"));
// Validates: { foo: string }Keeps only the specified keys from a Record type.
const Original = Type.Record({
foo: Type.String,
bar: Type.Number,
baz: Type.Boolean,
});
const Selected = compile(Pick(Original, "foo", "bar"));
// Validates: { foo: string, bar: number }Makes all properties of a Record type optional.
const Original = Type.Record({
name: Type.String,
age: Type.Number,
});
const OptionalProps = compile(Partial(Original));
// Validates: { name?: string, age?: number }Makes all properties of a Record type required (removes optionality).
const Original = Type.Record({
name: Type.Option(Type.String),
age: Type.Option(Type.Number),
});
const RequiredProps = compile(Required(Original));
// Validates: { name: string, age: number }Removes types from a union.
const Union = Type.OneOf(Type.String, Type.Number, Type.Boolean);
const StringOrBool = compile(Exclude(Union, Type.Number));
// Validates: string | booleanEach type can have metadata attached to it, such as titles, descriptions, and formats. Metadata is ignored during validation but is used by code generators.
const UserDT = Type.Record({
name: Type.String.meta.title("User Name").meta.description("The user's full name"),
email: Type.String.meta.title("Email").meta.format("email"),
}).meta.title("User")
.meta.description("A user account with contact information");import { getMetadata, Type } from "dilswer";
const EmailDT = Type.String.meta.title("Email Address").meta.format("email");
const metadata = getMetadata(EmailDT);
// metadata = {
// title: "Email Address",
// format: "email",
// }Generate JSON Schema from Dilswer types:
import { toJsonSchema, Type } from "dilswer";
const UserSchema = Type.Record({
name: Type.String.meta.title("Name"),
age: Type.Number,
});
const schema = toJsonSchema(UserSchema);
// schema = {
// type: "object",
// properties: {
// name: { type: "string", title: "Name" },
// age: { type: "number" },
// },
// required: ["name", "age"],
// }type ParseToJsonSchemaOptions = {
/** How to handle types without JSON Schema equivalents (Sets, Symbols, etc.) */
incompatibleTypes?: "throw" | "omit" | "set-as-any";
/** Whether to allow additional properties in records */
additionalProperties?: boolean;
/** Custom parsers for specific types */
customParser?: {
Set?: (schemas: JSONSchema6[], type: SetType, options: ParseToJsonSchemaOptions) => JSONSchema6 | undefined;
Custom?: (fn: Function, type: CustomType, options: ParseToJsonSchemaOptions) => JSONSchema6 | undefined;
Undefined?: (type: BasicType, options: ParseToJsonSchemaOptions) => JSONSchema6 | undefined;
Symbol?: (type: BasicType, options: ParseToJsonSchemaOptions) => JSONSchema6 | undefined;
Function?: (type: BasicType, options: ParseToJsonSchemaOptions) => JSONSchema6 | undefined;
};
};Generate TypeScript type definitions from Dilswer types:
import { toTsType, Type } from "dilswer";
const UserDT = Type.Record({
name: Type.String,
age: Type.Number,
}).meta.title("User");
const tsDefinition = toTsType(UserDT);
// Output:
// export type User = {
// name: string;
// age: number;
// };type TsParsingOptions = {
/** How to structure the output type */
mode?: "compact" | "fully-expanded" | "named-expanded";
/** Which types to export */
exports?: "main" | "named" | "all" | "none";
/** Generate type declarations */
declaration?: boolean;
/** How to handle duplicate names */
onDuplicateName?: "error" | "rename";
/** Custom import paths for external types */
getExternalTypeImport?: (type: EnumType | EnumMemberType | InstanceOfType | CustomType | FunctionType) => ExternalTypeImport | undefined;
};Dilswer types can be parsed into custom data structures using parseWith():
import { parseWith, Type } from "dilswer";
type TypeNode = {
typeName: string;
children?: TypeNode[] | Record<string, TypeNode>;
};
const visitor = {
visit(type: AnyType, children?: any): TypeNode {
switch (type.kind) {
case "simple":
return { typeName: type.simpleType };
case "record":
return {
typeName: "record",
children: children ? Object.fromEntries(children.map(({ propertyName, child }) => [propertyName, child])) : undefined,
};
default:
return { typeName: type.kind, children: children as TypeNode[] };
}
},
};
const MyType = Type.Record({
foo: Type.String,
bar: Type.Array(Type.Number),
});
const tree = parseWith(visitor, MyType);{
"typeName": "record",
"children": {
"foo": { "typeName": "string" },
"bar": { "typeName": "array", "children": [{ "typeName": "number" }]}
}
}