model-layer
Version:
186 lines (151 loc) • 4.92 kB
text/typescript
import {Type, IType, ITypeParams} from "./Type";
import {isNaN, invalidValuesAsString} from "../utils";
import {
ConflictFloorCeilRoundError,
ConflictNullAndZeroParameterError,
InvalidNumberError,
InvalidRoundError
} from "../errors";
export interface INumberTypeParams extends ITypeParams {
nullAsZero?: boolean;
zeroAsNull?: boolean;
ceil?: number;
round?: number;
floor?: number;
prepare?: (value: any, key: string, model: any) => number;
validate?:
((value: number, key: string) => boolean) |
RegExp
;
enum?: number[];
default?: number | (() => number);
}
export interface INumberType extends IType {
(params: INumberTypeParams): INumberType;
TOutput: number;
TInput: number;
TJson: number;
}
export class NumberType extends Type {
nullAsZero: boolean;
zeroAsNull: boolean;
ceil?: number;
round?: number;
floor?: number;
constructor({
nullAsZero = false,
zeroAsNull = false,
ceil,
round,
floor,
...otherParams
}: INumberTypeParams) {
super(otherParams);
this.nullAsZero = nullAsZero;
this.zeroAsNull = zeroAsNull;
if ( nullAsZero && zeroAsNull ) {
throw new ConflictNullAndZeroParameterError({});
}
if ( ceil != null ) {
if ( isNaN(+ceil) ) {
throw new InvalidRoundError({
roundType: "ceil",
invalidValue: invalidValuesAsString(ceil)
});
}
ceil = +ceil;
}
this.ceil = ceil;
if ( round != null ) {
if ( isNaN(+round) ) {
throw new InvalidRoundError({
roundType: "round",
invalidValue: invalidValuesAsString(round)
});
}
if ( ceil != null ) {
throw new ConflictFloorCeilRoundError({});
}
round = +round;
}
this.round = round;
if ( floor != null ) {
if ( isNaN(+floor) ) {
throw new InvalidRoundError({
roundType: "floor",
invalidValue: invalidValuesAsString(floor)
});
}
if ( ceil != null ) {
throw new ConflictFloorCeilRoundError({});
}
if ( round != null ) {
throw new ConflictFloorCeilRoundError({});
}
floor = +floor;
}
this.floor = floor;
}
prepare(originalValue: any, key: string) {
if ( originalValue == null ) {
if ( this.nullAsZero ) {
return 0;
}
return null;
}
let value: number | null = +originalValue;
if (
isNaN(value) ||
typeof originalValue === "boolean" ||
originalValue instanceof RegExp ||
originalValue instanceof Date ||
Array.isArray(originalValue) ||
// infinity
value === 1 / 0 ||
value === -1 / 0
) {
const valueAsString = invalidValuesAsString( originalValue );
throw new InvalidNumberError({
key,
invalidValue: valueAsString
});
}
if ( this.round != null ) {
const desc = Math.pow( 10, this.round );
value = Math.round(
value *
desc
) / desc;
}
else if ( this.floor != null ) {
const desc = Math.pow( 10, this.floor );
value = Math.floor(
value *
desc
) / desc;
}
else if ( this.ceil != null ) {
// 1.12 * 100 == 112.00000000000001
let valueStr = value.toString();
let commaPosition = valueStr.indexOf(".");
if ( commaPosition !== -1 ) {
commaPosition += this.ceil;
valueStr = valueStr.replace(".", "");
valueStr = (
valueStr.slice(0, commaPosition) +
"." +
valueStr.slice(commaPosition)
);
value = +valueStr;
const desc = Math.pow( 10, this.ceil );
value = Math.ceil(value) / desc;
}
}
if ( this.zeroAsNull ) {
if ( value === 0 ) {
value = null;
}
}
return value;
}
}