fix(schema-utils): add autocast string -> number

This commit is contained in:
Tomas Martykan
2024-01-26 10:35:21 +01:00
committed by Tomáš Martykán
parent 23b664a894
commit aaa0cd7846
4 changed files with 71 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ import { Mixin } from 'ts-mixer';
import { ArrayBufferBuilder, BufferBuilder, KeyofEnumBuilder, UintBuilder } from './custom-types';
import { InvalidParameter } from './errors';
import { setDeepValue } from './utils';
class CustomTypeBuilder extends Mixin(
JavaScriptTypeBuilder,
@@ -69,6 +70,17 @@ export function Assert<T extends TSchema>(schema: T, value: unknown): asserts va
} else if (error.type === ValueErrorType.Union) {
// Drill down into the union
FindErrorInUnion(error);
} else if (error.type === ValueErrorType.Number && typeof error.value === 'string') {
// String instead of number, try to autocast
const currentValue = error.value;
const parsedNumber = Number(currentValue);
if (!Number.isNaN(parsedNumber) && currentValue === parsedNumber.toString()) {
// Autocast successful
const pathParts = error.path.slice(1).split('/');
setDeepValue(value, pathParts, parsedNumber);
} else {
throw new InvalidParameter(error.message, error.path, error.type, error.value);
}
} else {
throw new InvalidParameter(error.message, error.path, error.type, error.value);
}

View File

@@ -0,0 +1,15 @@
/**
* Sets a value in an object by a path
* From https://stackoverflow.com/a/53762921
* @param obj object to set value in
* @param param path to the value
* @param value value to set
*/
export function setDeepValue(obj: any, [prop, ...path]: string[], value: any) {
if (!path.length) {
obj[prop] = value;
} else {
if (!(prop in obj)) obj[prop] = {};
setDeepValue(obj[prop], path, value);
}
}

View File

@@ -0,0 +1,23 @@
import { Type, Assert } from '../src';
describe('number-autocast', () => {
it('should string to number if needed', () => {
const schema = Type.Object({
number: Type.Number(),
nested: Type.Object({
number: Type.Number(),
}),
});
const input = {
number: '1',
nested: {
number: '1',
},
};
Assert(schema, input);
expect(input.number).toEqual(1);
expect(input.nested.number).toEqual(1);
});
});

View File

@@ -0,0 +1,21 @@
import { setDeepValue } from '../src/utils';
describe('setDeepValue', () => {
it('sets a deep value in an object', () => {
const obj = {};
setDeepValue(obj, ['a', 'b', 'c'], 123);
expect(obj).toEqual({ a: { b: { c: 123 } } });
});
it('overwrites existing values', () => {
const obj = { a: { b: { c: 123 } } };
setDeepValue(obj, ['a', 'b', 'c'], 456);
expect(obj).toEqual({ a: { b: { c: 456 } } });
});
it('creates intermediate objects if necessary', () => {
const obj = {};
setDeepValue(obj, ['a', 'b', 'c'], 123);
expect(obj).toEqual({ a: { b: { c: 123 } } });
});
});