@tldraw/validate
Version:
A runtime validation library by tldraw.
408 lines (363 loc) • 11 kB
text/typescript
import { IndexKey } from '@tldraw/utils';
import { JsonValue } from '@tldraw/utils';
import { MakeUndefinedOptional } from '@tldraw/utils';
/**
* Validation that accepts any value. Generally this should be avoided, but you can use it as an
* escape hatch if you want to work without validations for e.g. a prototype.
*
* @public
*/
declare const any: Validator<any>;
/**
* Validates that a value is an array. To check the contents of the array, use T.arrayOf.
*
* @public
*/
declare const array: Validator<unknown[]>;
/**
* Validates that a value is an array whose contents matches the passed-in validator.
*
* @public
*/
declare function arrayOf<T>(itemValidator: Validatable<T>): ArrayOfValidator<T>;
/** @public */
export declare class ArrayOfValidator<T> extends Validator<T[]> {
readonly itemValidator: Validatable<T>;
constructor(itemValidator: Validatable<T>);
nonEmpty(): Validator<T[]>;
lengthGreaterThan1(): Validator<T[]>;
}
/**
* Validates that a value is a bigint.
*
* @public
*/
declare const bigint: Validator<bigint>;
/**
* Validates that a value is boolean.
*
* @public
*/
declare const boolean: Validator<boolean>;
/**
* Validation that an option is a dict with particular keys and values.
*
* @public
*/
declare function dict<Key extends string, Value>(keyValidator: Validatable<Key>, valueValidator: Validatable<Value>): DictValidator<Key, Value>;
/** @public */
export declare class DictValidator<Key extends string, Value> extends Validator<Record<Key, Value>> {
readonly keyValidator: Validatable<Key>;
readonly valueValidator: Validatable<Value>;
constructor(keyValidator: Validatable<Key>, valueValidator: Validatable<Value>);
}
/**
* Validates an http(s) url
*
* @public
*/
declare const httpUrl: Validator<string>;
/**
* Validates that a value is an IndexKey.
* @public
*/
declare const indexKey: Validator<IndexKey>;
/**
* Fails if number is not an integer
*
* @public
*/
declare const integer: Validator<number>;
/**
* Validate an object has a particular shape.
*
* @public
*/
declare function jsonDict(): DictValidator<string, JsonValue>;
/**
* Validate that a value is valid JSON.
*
* @public
*/
declare const jsonValue: Validator<JsonValue>;
/**
* Validates that a value is a url safe to use as a link.
*
* @public
*/
declare const linkUrl: Validator<string>;
/**
* Validates that a value matches another that was passed in.
*
* @example
*
* ```ts
* const trueValidator = T.literal(true)
* ```
*
* @public
*/
declare function literal<T extends boolean | number | string>(expectedValue: T): Validator<T>;
/** @public */
declare function literalEnum<const Values extends readonly unknown[]>(...values: Values): Validator<Values[number]>;
/**
* A named object with an ID. Errors will be reported as being part of the object with the given
* name.
*
* @public
*/
declare function model<T extends {
readonly id: string;
}>(name: string, validator: Validatable<T>): Validator<T>;
/**
* Fails if value \<= 0 and is not an integer
*
* @public
*/
declare const nonZeroInteger: Validator<number>;
/**
* Fails if value \<= 0
*
* @public
*/
declare const nonZeroNumber: Validator<number>;
/** @public */
declare function nullable<T>(validator: Validatable<T>): Validator<null | T>;
/**
* Validates that a value is a finite non-NaN number.
*
* @public
*/
declare const number: Validator<number>;
/* Excluded from this release type: numberUnion */
/**
* Validate an object has a particular shape.
*
* @public
*/
declare function object<Shape extends object>(config: {
readonly [K in keyof Shape]: Validatable<Shape[K]>;
}): ObjectValidator<MakeUndefinedOptional<Shape>>;
/** @public */
export declare class ObjectValidator<Shape extends object> extends Validator<Shape> {
readonly config: {
readonly [K in keyof Shape]: Validatable<Shape[K]>;
};
private readonly shouldAllowUnknownProperties;
constructor(config: {
readonly [K in keyof Shape]: Validatable<Shape[K]>;
}, shouldAllowUnknownProperties?: boolean);
allowUnknownProperties(): ObjectValidator<Shape>;
/**
* Extend an object validator by adding additional properties.
*
* @example
*
* ```ts
* const animalValidator = T.object({
* name: T.string,
* })
* const catValidator = animalValidator.extend({
* meowVolume: T.number,
* })
* ```
*/
extend<Extension extends Record<string, unknown>>(extension: {
readonly [K in keyof Extension]: Validatable<Extension[K]>;
}): ObjectValidator<Shape & Extension>;
}
/** @public */
declare function optional<T>(validator: Validatable<T>): Validator<T | undefined>;
/**
* Validate a value against one of two types.
*
* @public
*/
declare function or<T1, T2>(v1: Validatable<T1>, v2: Validatable<T2>): Validator<T1 | T2>;
/**
* Fails if value \< 0 and is not an integer
*
* @public
*/
declare const positiveInteger: Validator<number>;
/**
* Fails if value \< 0
*
* @public
*/
declare const positiveNumber: Validator<number>;
/** @public */
declare function setEnum<T>(values: ReadonlySet<T>): Validator<T>;
/**
* Validates that a valid is a url safe to load as an asset.
*
* @public
*/
declare const srcUrl: Validator<string>;
/**
* Validates that a value is a string.
*
* @public
*/
declare const string: Validator<string>;
declare namespace T {
export {
literal,
arrayOf,
object,
jsonDict,
dict,
union,
model,
setEnum,
optional,
nullable,
literalEnum,
or,
ValidatorFn,
ValidatorUsingKnownGoodVersionFn,
Validatable,
ValidationError,
TypeOf,
Validator,
ArrayOfValidator,
ObjectValidator,
UnionValidatorConfig,
UnionValidator,
DictValidator,
unknown,
any,
string,
number,
positiveNumber,
nonZeroNumber,
integer,
positiveInteger,
nonZeroInteger,
boolean,
bigint,
array,
unknownObject,
jsonValue,
linkUrl,
srcUrl,
httpUrl,
indexKey
}
}
export { T }
/** @public */
declare type TypeOf<V extends Validatable<any>> = V extends Validatable<infer T> ? T : never;
/**
* Validate a union of several object types. Each object must have a property matching `key` which
* should be a unique string.
*
* @example
*
* ```ts
* const catValidator = T.object({ kind: T.literal('cat'), meow: T.boolean })
* const dogValidator = T.object({ kind: T.literal('dog'), bark: T.boolean })
* const animalValidator = T.union('kind', { cat: catValidator, dog: dogValidator })
* ```
*
* @public
*/
declare function union<Key extends string, Config extends UnionValidatorConfig<Key, Config>>(key: Key, config: Config): UnionValidator<Key, Config>;
/** @public */
export declare class UnionValidator<Key extends string, Config extends UnionValidatorConfig<Key, Config>, UnknownValue = never> extends Validator<TypeOf<Config[keyof Config]> | UnknownValue> {
private readonly key;
private readonly config;
private readonly unknownValueValidation;
private readonly useNumberKeys;
constructor(key: Key, config: Config, unknownValueValidation: (value: object, variant: string) => UnknownValue, useNumberKeys: boolean);
private expectObject;
private getMatchingSchemaAndVariant;
validateUnknownVariants<Unknown>(unknownValueValidation: (value: object, variant: string) => Unknown): UnionValidator<Key, Config, Unknown>;
}
/** @public */
export declare type UnionValidatorConfig<Key extends string, Config> = {
readonly [Variant in keyof Config]: Validatable<any> & {
validate(input: any): {
readonly [K in Key]: Variant;
};
};
};
/**
* Validation that accepts any value. Useful as a starting point for building your own custom
* validations.
*
* @public
*/
declare const unknown: Validator<unknown>;
/** @public */
declare const unknownObject: Validator<Record<string, unknown>>;
/** @public */
declare interface Validatable<T> {
validate(value: unknown): T;
/**
* This is a performance optimizing version of validate that can use a previous
* version of the value to avoid revalidating every part of the new value if
* any part of it has not changed since the last validation.
*
* If the value has not changed but is not referentially equal, the function
* should return the previous value.
* @returns
*/
validateUsingKnownGoodVersion?(knownGoodValue: T, newValue: unknown): T;
}
/** @public */
declare class ValidationError extends Error {
readonly rawMessage: string;
readonly path: ReadonlyArray<number | string>;
name: string;
constructor(rawMessage: string, path?: ReadonlyArray<number | string>);
}
/** @public */
export declare class Validator<T> implements Validatable<T> {
readonly validationFn: ValidatorFn<T>;
readonly validateUsingKnownGoodVersionFn?: undefined | ValidatorUsingKnownGoodVersionFn<T>;
constructor(validationFn: ValidatorFn<T>, validateUsingKnownGoodVersionFn?: undefined | ValidatorUsingKnownGoodVersionFn<T>);
/**
* Asserts that the passed value is of the correct type and returns it. The returned value is
* guaranteed to be referentially equal to the passed value.
*/
validate(value: unknown): T;
validateUsingKnownGoodVersion(knownGoodValue: T, newValue: unknown): T;
/** Checks that the passed value is of the correct type. */
isValid(value: unknown): value is T;
/**
* Returns a new validator that also accepts null or undefined. The resulting value will always be
* null.
*/
nullable(): Validator<null | T>;
/**
* Returns a new validator that also accepts null or undefined. The resulting value will always be
* null.
*/
optional(): Validator<T | undefined>;
/**
* Refine this validation to a new type. The passed-in validation function should throw an error
* if the value can't be converted to the new type, or return the new type otherwise.
*/
refine<U>(otherValidationFn: (value: T) => U): Validator<U>;
/**
* Refine this validation with an additional check that doesn't change the resulting value.
*
* @example
*
* ```ts
* const numberLessThan10Validator = T.number.check((value) => {
* if (value >= 10) {
* throw new ValidationError(`Expected number less than 10, got ${value}`)
* }
* })
* ```
*/
check(name: string, checkFn: (value: T) => void): Validator<T>;
check(checkFn: (value: T) => void): Validator<T>;
}
/** @public */
declare type ValidatorFn<T> = (value: unknown) => T;
/** @public */
declare type ValidatorUsingKnownGoodVersionFn<In, Out = In> = (knownGoodValue: In, value: unknown) => Out;
export { }