Created
October 13, 2022 11:14
-
-
Save honungsburk/d8cc687f660585d9901d52ad022782db to your computer and use it in GitHub Desktop.
A typescript validation object where you can only call methods once
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Entry point for an integer parser | |
* | |
* @param error - error to throw if the parsed value is not an integer | |
* @returns a parser for integers | |
*/ | |
export function int(error?: (s: string) => any): typeof ValidatorBuilderNumber { | |
const obj = { ...ValidatorBuilderNumber }; | |
obj[onParseError] = error; | |
return obj; | |
} | |
// By defining a Symbol we can create private functions/variables since no one | |
// outside this file has access to this symbol! | |
const onParseError = Symbol(); | |
const onRequiredError = Symbol(); | |
const minV = Symbol(); | |
const onMinError = Symbol(); | |
const maxV = Symbol(); | |
const onMaxError = Symbol(); | |
/** | |
* The base class contains all variables and the parse function that uses them. | |
* We must seperate the class into two parts so that we can self-reference | |
*/ | |
const ValidatorBuilderNumberBase = { | |
// Problem is that you can see what should be private variables... | |
[onRequiredError]: undefined as undefined | any, | |
[onParseError]: undefined as undefined | ((s: string) => any), | |
[minV]: undefined as undefined | number, | |
[onMinError]: undefined as undefined | ((n: number) => any), | |
[maxV]: undefined as undefined | number, | |
[onMaxError]: undefined as undefined | ((n: number) => any), | |
/** | |
* Parse your string into either a number or number | undefined | |
* | |
* @param value - the value to parse | |
* @param onUndefined - if the return value would be undefined, throw this instead | |
* @returns if onUndefined is defined it will return a number, otherwise number or undefined | |
*/ | |
parse<A = any>( | |
value?: any, | |
onUndefined?: A | |
): A extends undefined ? number | undefined : number { | |
// required check | |
if (value === undefined) { | |
if (this[onRequiredError]) { | |
throw this[onRequiredError]; | |
} | |
return throwIfNotUndefined(onUndefined); | |
} | |
if (typeof value !== "string") { | |
if (this[onParseError]) { | |
throw this[onParseError](value); | |
} | |
return throwIfNotUndefined(onUndefined); | |
} | |
let n: number; | |
// Parse | |
try { | |
n = parseInt(value); | |
} catch (err) { | |
if (this[onParseError]) { | |
throw this[onParseError](value); | |
} | |
return throwIfNotUndefined(onUndefined); | |
} | |
//min check | |
if (this[minV] !== undefined && this[minV] > n) { | |
if (this[onMinError]) { | |
throw this[onMinError](n); | |
} | |
return throwIfNotUndefined(onUndefined); | |
} | |
//max check | |
if (this[maxV] !== undefined && this[maxV] < n) { | |
if (this[onMaxError]) { | |
throw this[onMaxError](n); | |
} | |
return throwIfNotUndefined(onUndefined); | |
} | |
return n; | |
}, | |
}; | |
function throwIfNotUndefined(onUndefined: any): any { | |
if (onUndefined !== undefined) { | |
throw onUndefined; | |
} else { | |
return undefined as any; | |
} | |
} | |
/** | |
* This class removes functions after they are called. | |
*/ | |
const ValidatorBuilderNumber = { | |
...ValidatorBuilderNumberBase, | |
/** | |
* | |
* @param n the minimal value the parser will allow | |
* @param error the error to throw if the value is out of bounds | |
* @returns a NumberParserBuilder | |
*/ | |
min<T extends typeof ValidatorBuilderNumberBase & { min: unknown }>( | |
this: T, | |
n: number, | |
error?: (n: number) => any | |
) { | |
const { min, ..._this } = this; | |
return { ..._this, [minV]: n, [onMinError]: error }; | |
}, | |
/** | |
* | |
* @param n the maximal value the parser will allow | |
* @param error the error to throw if the value is out of bounds | |
* @returns a NumberParserBuilder | |
*/ | |
max<T extends typeof ValidatorBuilderNumberBase & { max: unknown }>( | |
this: T, | |
n: number, | |
error?: (n: number) => any | |
) { | |
const { max, ..._this } = this; | |
return { ..._this, [maxV]: n, [onMaxError]: error }; | |
}, | |
/** | |
* | |
* @param error - thow this error if value is required | |
* @returns a NumberParserBuilder | |
*/ | |
required<T extends typeof ValidatorBuilderNumberBase & { required: unknown }>( | |
this: T, | |
error: { [key: string]: string | number | boolean } | |
) { | |
const { required, ..._this } = this; | |
return { ..._this, [onRequiredError]: error }; | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example usage: