runtypes
Version:
Runtime validation for static types
89 lines (88 loc) • 4.25 kB
TypeScript
import Optional from "./Optional.js";
import Runtype, { type Parsed, type Static } from "./Runtype.js";
type OptionalKeysStatic<T> = {
[K in keyof T]: T[K] extends Optional ? K : never;
}[keyof T];
type RequiredKeysStatic<T> = {
[K in keyof T]: T[K] extends Optional ? never : K;
}[keyof T];
type OptionalKeysParsed<T> = {
[K in keyof T]: T[K] extends Optional<any, infer X> ? ([X] extends [never] ? K : never) : never;
}[keyof T];
type RequiredKeysParsed<T> = {
[K in keyof T]: T[K] extends Optional<any, infer X> ? ([X] extends [never] ? never : K) : K;
}[keyof T];
type RequiredValuesStatic<T> = T extends Runtype.Core ? Static<T> : never;
type OptionalValuesStatic<T> = T extends Optional<infer OK> ? Static<OK> : never;
type RequiredValuesParsed<T> = T extends Optional<infer OK, infer X> ? [X] extends [never] ? never : Parsed<OK> | X : T extends Runtype.Core ? Parsed<T> : never;
type OptionalValuesParsed<T> = T extends Optional<infer OK, infer X> ? ([X] extends [never] ? Parsed<OK> : never) : never;
type ObjectStaticReadonly<O extends Object.Fields> = {
[K in RequiredKeysStatic<O>]: RequiredValuesStatic<O[K]>;
} & {
[K in OptionalKeysStatic<O>]?: OptionalValuesStatic<O[K]>;
} extends infer P ? {
readonly [K in keyof P]: P[K];
} : never;
type ObjectStatic<O extends Object.Fields> = {
[K in RequiredKeysStatic<O>]: RequiredValuesStatic<O[K]>;
} & {
[K in OptionalKeysStatic<O>]?: OptionalValuesStatic<O[K]>;
} extends infer P ? {
[K in keyof P]: P[K];
} : never;
type ObjectParsedReadonly<O extends Object.Fields> = {
[K in RequiredKeysParsed<O>]: RequiredValuesParsed<O[K]>;
} & {
[K in OptionalKeysParsed<O>]?: OptionalValuesParsed<O[K]>;
} extends infer P ? {
readonly [K in keyof P]: P[K];
} : never;
type ObjectParsed<O extends Object.Fields> = {
[K in RequiredKeysParsed<O>]: RequiredValuesParsed<O[K]>;
} & {
[K in OptionalKeysParsed<O>]?: OptionalValuesParsed<O[K]>;
} extends infer P ? {
[K in keyof P]: P[K];
} : never;
type Utilities<O extends Object.Fields> = {
asPartial(): Object<{
[K in keyof O]: O[K] extends Optional ? O[K] : O[K] extends Runtype.Core ? Optional<O[K]> : never;
}>;
asReadonly(): Object.Readonly<O>;
pick<K extends keyof O = never>(...keys: K[]): Object<Pick<O, K>>;
omit<K extends keyof O = never>(...keys: K[]): Object<Omit<O, K>>;
extend<P extends Object.Fields>(fields: {
[K in keyof P]: K extends keyof O ? O[K] extends Optional<infer OK> ? P[K] extends Optional<infer PK> ? Static<PK> extends Static<OK> ? P[K] : Runtype.Core<Static<OK>> | Optional<Runtype.Core<Static<OK>>> : P[K] extends Runtype.Core ? P[K] extends OK ? P[K] : Runtype.Core<Static<OK>> | Optional<Runtype.Core<Static<OK>>> : never : O[K] extends Runtype.Core ? P[K] extends Optional ? Runtype.Core<Static<O[K]>> : P[K] extends Runtype.Core ? Static<P[K]> extends Static<O[K]> ? P[K] : Runtype.Core<Static<O[K]>> : never : never : P[K];
}): Object<{
[K in keyof O | keyof P]: K extends keyof P ? P[K] : K extends keyof O ? O[K] : never;
}>;
exact(): Object<O>;
};
/**
* Validates that a value is an object and each property fulfills the given property runtype.
*
* Possible failures:
*
* - `TYPE_INCORRECT` for `null`, `undefined`, and non-objects if fields were non-empty
* - `CONTENT_INCORRECT` with `details` reporting the failed properties
*
* For each property, contextual failures can be seen in addition to failures of the property runtype:
*
* - `PROPERTY_MISSING` for missing required properties
* - `PROPERTY_PRESENT` for extraneous properties where `.exact()` flag is enabled
*/
interface Object<O extends Object.Fields = Object.Fields> extends Runtype<ObjectStatic<O>, ObjectParsed<O>>, Utilities<O> {
tag: "object";
fields: O;
isExact: boolean;
}
declare namespace Object {
type Fields = globalThis.Record<PropertyKey, Runtype.Core | Optional>;
interface Readonly<O extends Object.Fields = Object.Fields> extends Runtype<ObjectStaticReadonly<O>, ObjectParsedReadonly<O>>, Utilities<O> {
tag: "object";
fields: O;
isExact: boolean;
}
}
declare const Object: <O extends Object.Fields>(fields: O) => Object<O>;
export default Object;