Standard libraries that lift up your code.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

80 lines
1.8 KiB

import {Integer, isInteger} from './types'
import {ErrorWithContext} from '../error/ErrorWithContext'
import {JSONState} from './Rehydratable'
import {isJSON} from './data'
export class Safe {
protected thrower: (message: string, value: unknown) => never
constructor(
protected readonly value: unknown,
) {
this.thrower = (message) => {
throw new ErrorWithContext('Invalid value', {
message,
value,
})
}
}
onError(thrower: (message: string, value: unknown) => never): this {
this.thrower = thrower
return this
}
present(): this {
if ( !this.value && this.value !== 0 && this.value !== false ) {
this.thrower('Missing value', this.value)
}
return this
}
integer(): Integer {
const value = parseInt(String(this.value), 10)
if ( !isInteger(value) ) {
this.thrower('Invalid integer', this.value)
}
return value
}
number(): number {
const value = parseFloat(String(this.value))
if ( isNaN(value) ) {
this.thrower('Invalid number', this.value)
}
return value
}
string(): string {
this.present()
return String(this.value)
}
json(): JSONState {
const str = this.string()
if ( !isJSON(str) ) {
this.thrower('Invalid JSON', str)
}
return JSON.parse(str)
}
or(other: unknown): Safe {
if ( !this.value && this.value !== 0 && this.value !== false ) {
return new Safe(other)
}
return this
}
in<T>(allowed: T[]): T {
if ( allowed.includes(this.value as any) ) {
return this.value as T
}
this.thrower('Invalid value', this.value)
}
}