export type TyCheck = (value: unknown) => T; const assertType = (wanted: string) => (value: unknown): T => { const passed = typeof value === wanted; const given = typeof value; if (!passed) { throw new Error(`Type is incorrect, wanted ${wanted} but given ${given}`); } return value as T; }; const assertArray = (value: unknown): T[] => { if (!Array.isArray(value)) { throw new Error('Type not an array'); } return value as T[]; }; const fieldExists = (object: Record) => (field: string): unknown => { const value = object[field]; if (value === undefined) { throw new Error('missing value'); } return value; }; const fieldCheck = (obj: Record) => ([field, check]: [string, TyCheck]): void => { try { check(fieldExists(obj)(field)); } catch (e) { const parent = e as Error; throw new Error(`On field "${field}": ${parent.message}`); } }; type ObjectTypeChecker> = { [P in keyof T]: TyCheck }; export const typeString: TyCheck = assertType('string'); export const typeNumber: TyCheck = assertType('number'); export const typeBoolean: TyCheck = assertType('boolean'); export const typeObject: TyCheck = assertType('object'); export const beInterface = >(checker: ObjectTypeChecker): TyCheck => (value: unknown) => { const obj = typeObject(value) as Record; Object.entries(checker).forEach(fieldCheck(obj)); return value as T; }; export const beArray = (checker: TyCheck): TyCheck => (value: unknown) => { const array = assertArray(value); array.forEach(checker); return array as T[]; }; export const beOption = (checker: TyCheck): TyCheck => (value: unknown) => { if (value !== undefined) { checker(value); } return value as T; }; export const typeChecker = (typeCheck: TyCheck) => (value: unknown): T => typeCheck(value);