export type Schema = (value: unknown) => value is T; export const string: Schema = (value) => typeof value === "string"; export const boolean: Schema = (value) => typeof value === "boolean"; export const finiteNumber: Schema = (value): value is number => typeof value === "number" && Number.isFinite(value); export const record: Schema> = ( value, ): value is Record => typeof value === "object" && value !== null; export const literal = (...values: T[]): Schema => (value): value is T => values.includes(value as T); export const arrayOf = (item: Schema): Schema => (value): value is T[] => Array.isArray(value) && value.every(item); export const recordOf = (item: Schema): Schema> => (value): value is Record => record(value) && Object.values(value).every(item); export const nonEmpty = (schema: Schema): Schema => (value): value is T => schema(value) && value.length > 0; export const optional = (schema: Schema): Schema => (value): value is T | undefined => value === undefined || schema(value); export const object = (shape: { [K in keyof T]-?: Schema }): Schema => (value): value is T => record(value) && Object.entries>(shape as never).every(([key, check]) => check(value[key]), );