Last active
January 9, 2025 06:59
-
-
Save jmakeig/16897238002bd89bd28aa60b402cff70 to your computer and use it in GitHub Desktop.
Experiment to return validation assertions from API call versus throwing exceptions
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
type Nullable<T> = { [P in keyof T]: T[P] | null; } | |
type Locale = 'en-US'; | |
type Message = string | { | |
[key in Locale]: string; | |
} | |
type Validation<Entity> = { | |
for?: keyof Entity; | |
message: Message; | |
} | |
/** | |
* The return type of an API call. For invalid results, it reflects back what was passed in (`In`) as `value` along with a `validations` property, | |
* listing the validation assertions. | |
* If the result is valid it returns the entity (`Out`). | |
* Make sure your entity (`Out`) doesn’t have a `validations` property itself. See `is_valid()`. | |
*/ | |
type Result<In, Out> = Out | InvalidResult<In, Out>; | |
class InvalidResult<In, Out> { | |
validations: Array<Validation<Out>>; | |
value: In; | |
constructor(value: In, validations: Array<Validation<Out>>) { | |
this.value = value; | |
this.validations = validations; | |
} | |
} | |
type Customer = { | |
customer: string; | |
name: string; | |
label: string; | |
segment: string | null; | |
region: string | null; | |
} | |
function validate_customer(customer: Nullable<Customer>): Array<Validation<Customer>> { | |
const validations: Array<Validation<Customer>> = []; | |
if ((customer.name ?? '').length < 3) validations.push({ for: 'name', message: 'Short' }); | |
if ((customer.label ?? '').length < 2) validations.push({ for: 'label', message: 'Short' }); | |
return validations; | |
} | |
function add_customer(customer: Nullable<Customer>): Result<Nullable<Customer>, Customer> { | |
function is_valid_customer(customer: Nullable<Customer>, validations: Array<Validation<Customer>>): customer is Customer { | |
return customer && 0 === validations.length; | |
} | |
const validations = validate_customer(customer); | |
if (!is_valid_customer(customer, validations)) { | |
return new InvalidResult(customer, validations); | |
} | |
// Simulate database call | |
return Object.assign({}, customer); | |
} | |
/** | |
* Whether a result contains a validation | |
*/ | |
function is_invalid<T>(result: Result<unknown, T>): result is T { | |
return result instanceof InvalidResult; | |
} | |
const invalid: Nullable<Customer> = { customer: null, name: null, label: null, segment: null, region: null }; | |
//const valid: Nullable<Customer> = { customer: null, name: 'Aaa', label: 'aaa', segment: null, region: null }; | |
const processed: Result<Nullable<Customer>, Customer> = add_customer(invalid); | |
if (is_invalid(processed)) { | |
console.error('INVALID', processed); | |
} else { | |
console.log('VALID', processed); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note that
is_valid()
should only be used to sniff the runtime type of the payload. It shouldn’t be used to actually determine validity. The latter isis_valid_customer()
above.