import { Checker , JsonTypeOf } from './types' ; export const string = { check : ( value ) : value is string => { return typeof value === 'string' ; } , } ; export const number = { check : ( value ) : value is number => { return typeof value === 'number' ; } , } ; export const boolean = { check : ( value ) : value is boolean => { return typeof value === 'boolean' ; } , } ; export const nil = { check : ( value ) : value is null => { return value === null ; } , } ; export const unknown = { check : ( value ) : value is unknown => { return true ; } , } ; export function literal < T extends string > ( str ) { return { check ( value ) { return value === str ; } , } ; } export function any < T extends Checker < unknown > > ( checkers ) { return { check ( value ) { return checkers . some ( checker => checker . check ( value ) ) ; } , } ; } export function nullable < T > ( checker ) { return { check ( value ) { return value === null || checker . check ( value ) ; } , } ; } export function array < T extends Checker < unknown > > ( checker ) { return { check ( value ) { return Array . isArray ( value ) && value . every ( it => checker . check ( it ) ) ; } , } ; } export function object < T extends { [ key ] : Checker < unknown > | Function ; } > ( properties ) { return { check ( value ) { if ( typeof value !== 'object' || value === null ) return false ; return Object . keys ( properties ) . every ( key => { const unescapedKey = unescapePropertyName ( key ) ; if ( has ( value , unescapedKey ) ) { const checkerOrFunction = properties [ key ] ; if ( isChecker ( checkerOrFunction ) ) { return checkerOrFunction . check ( value [ unescapedKey ] ) ; } const lazyChecker = checkerOrFunction ( ) ; if ( isChecker ( lazyChecker ) ) { return lazyChecker . check ( value [ unescapedKey ] ) ; } else { return false ; } } else { return isOptionalProperty ( key ) ; } } ) ; } , } ; } function isChecker ( target ) { return ( typeof target === 'object' && target !== null && has ( target , 'check' ) && typeof target . check === 'function' ) ; } type FilterOptionalPropertyName < T > = T extends `${ infer U }??` ? `${ FilterOptionalPropertyName < U > }?` : T extends `${ infer U }?` ? U : never ; type FilterRequiredPropertyName < T > = T extends `${ infer U }??` ? `${ FilterRequiredPropertyName < U > }?` : T extends `${ infer U }?` ? never : T ; type ObjectJsonTypeOf < T extends { [ k ] : Checker < unknown > | Function ; } > = { [ K in keyof T as FilterRequiredPropertyName < K > ] - ? : T [ K ] extends Checker < unknown > ? JsonTypeOf < T [ K ] > : T [ K ] extends Function ? LazyChecker < T [ K ] > : never ; } & { [ K in keyof T as FilterOptionalPropertyName < K > ] + ? : T [ K ] extends Checker < unknown > ? JsonTypeOf < T [ K ] > : T [ K ] extends Function ? LazyChecker < T [ K ] > : never ; } ; type LazyChecker < T extends Function > = T extends ( ) => Checker < infer U > ? U : never ; type ExpandRecursively < T > = T extends object ? T extends infer O ? { [ K in keyof O ] : ExpandRecursively < O [ K ] > ; } : never : T ; export function isOptionalProperty ( propertyName ) { return propertyName . match ( /[^?]?(?:\?\?)*(\?)?$/ ) ?. [ 1 ] === '?' ; } export function unescapePropertyName ( propertyName ) { const match = propertyName . match ( /^(.*?[^?]?)((?:\?\?)*)\??$/ ) ; if ( match === null ) { return propertyName ; } const head = match [ 1 ] ?? '' ; const questionMarks = match [ 2 ] . replace ( /\?\?/g , '?' ) ?? '' ; return `${ head }${ questionMarks }` ; } function has < P extends PropertyKey > ( target , property ) { return property in target ; }