transformation-utils
Version:
A utility library for data transformation, type coercion, and value manipulation
161 lines (150 loc) • 4.72 kB
JavaScript
import { StringifyValue } from './stringifyValue.js';
import { StrictNumberConverter } from './strictNumberConverter.js';
import error from '../helpers/errors.js';
export class CoerceToType {
#value;
#targetType;
#valueType;
constructor(value, targetType) {
this.#value = value;
this.#valueType = typeof value;
this.#targetType = targetType;
}
coerce() {
this.#setTargetType(this.#targetTypeCheck(this.#targetType));
return this.#coerce();
}
#setTargetType(newTargetType) {
this.#targetType = newTargetType;
}
#targetTypeCheck(targetType) {
if (typeof targetType === 'string') {
return targetType.toLowerCase();
}
error('Name of a target type must be a string');
}
#coerce() {
switch (this.#targetType) {
case 'string':
return new StringifyValue(this.#value).toString();
case 'number':
try {
return new StrictNumberConverter(this.#value).convertToNumber();
} catch (err) {
error(err.message);
}
break;
case 'boolean':
if (this.#valueType === 'boolean') return this.#value;
else if (this.#valueType === 'string') {
const v = this.#value.trim().toLowerCase();
if (v === 'true') {
return true;
} else if (v === 'false') {
return false;
} else {
error("Value must be 'true' or 'false'");
}
} else {
error(`Cannot coerce type ${this.#valueType} to ${this.#targetType}`);
}
break;
case 'bigint':
switch (this.#valueType) {
case 'bigint':
return this.#value;
case 'number':
if (!Number.isInteger(this.#value)) {
error('Value must be an integer');
}
return BigInt(this.#value);
case 'string': {
const v = Number.parseFloat(this.#value);
if (isNaN(v)) {
error('String must start with a digits');
} else if (!Number.isInteger(v)) {
error('Value must be an integer');
} else {
return BigInt(v);
}
break;
}
case 'object': {
const number = new StrictNumberConverter(
this.#value,
).convertToNumber();
if (!Number.isInteger(number)) {
error(`This object cannot be converted to a bigint`);
}
return BigInt(number);
}
default:
error(
`Cannot coerce type ${this.#valueType} to ${this.#targetType}`,
);
}
break;
case 'symbol':
if (this.#valueType === 'symbol') {
return this.#value;
} else {
error(`Cannot coerce type ${this.#valueType} to ${this.#targetType}`);
}
break;
case 'array':
if (Array.isArray(this.#value)) return this.#value;
if (this.#valueType === 'string') return Array.from(this.#value);
if (this.#valueType === 'object' && this.#value !== null)
return Object.values(this.#value);
if (
this.#valueType === 'number' ||
this.#valueType === 'bigint' ||
this.#valueType === 'boolean'
)
return [this.#value];
return error(
`Cannot coerce type ${
this.#value === null ? 'null' : this.#valueType
} to ${this.#targetType}`,
);
case 'object':
switch (this.#valueType) {
case 'object':
if (this.#value === null) {
error('Cannot coerce null to object');
}
return this.#value;
case 'string': {
try {
return JSON.parse(this.#value);
} catch (error) {
if (error instanceof SyntaxError) {
const result = {};
for (let i = 0; i < this.#value.length; i++) {
result[i] = this.#value[i];
}
return result;
}
error(error.message);
}
break;
}
case 'number':
return { number: this.#value };
case 'boolean':
return { boolean: this.#value };
case 'bigint':
return { bigint: this.#value };
case 'symbol':
return { symbol: this.#value };
default:
error(
`Cannot coerce type ${this.#valueType} to ${this.#targetType}`,
);
}
break;
default:
error(`Invalid target type: ${this.#targetType}`);
}
}
}