diff --git a/README.md b/README.md index 74d67aa..a78f75c 100644 --- a/README.md +++ b/README.md @@ -50,19 +50,25 @@ const parsedParameters = parameters.parse({ //=> { TOKEN: 'xxxx', FIREBASE_CONFIG: { apiKey: 'xxxx' } } ``` -### API +## API see `test/index.spec.ts`. -```ts -import { createTypedParameters } from 'construct-typed-parameters'; +### createTypedParameters -// type ParameterValidate = (value: T) => string | string[] | null; +```ts +import { + createTypedParameters, + TypedParametersConstruct, +} from 'construct-typed-parameters'; -const parameters = createTypedParameters(pt => ({ +const parameters: TypedParametersConstruct = createTypedParameters(pt => ({ stringValue: pt.String({ + // required: boolean required: true, + // defaultValue?: T1 defaultValue: 'xxxx', + // validate?: (value: T1) => string | string[] | null; validate: v => (v.includes('x') ? null : 'the value must contain x'), }), unionStringValue: pt.UnionString<'v1' | 'v2'>({ @@ -99,6 +105,28 @@ const parameters = createTypedParameters(pt => ({ })); ``` +### TypedParametersConstruct#parse + +```ts +const parameters: TypedParametersConstruct = createTypedParameters(pt => ({ ... })); + +parameters.parse( + stringifiedParameters: Partial>, + shouldValidate = true +) : ParsedParameters +``` + +### TypedParametersConstruct#stringify + +```ts +const parameters: TypedParametersConstruct = createTypedParameters(pt => ({ ... })); + +parameters.stringify( + parsedParameters: Partial>, + shouldValidate = true +) : StringifiedParameters +``` + [build-img]: https://github.com/masahirompp/construct-typed-parameters/actions/workflows/release.yml/badge.svg [build-url]: https://github.com/masahirompp/construct-typed-parameters/actions/workflows/release.yml [downloads-img]: https://img.shields.io/npm/dt/construct-typed-parameters diff --git a/src/index.ts b/src/index.ts index c329af5..950228a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -136,14 +136,17 @@ const ParameterType = { class TypedParameters>> { constructor(private parametersConstruct: T) {} - parse(serializedParameters: Partial>) { + parse( + stringifiedParameters: Partial>, + shouldValidate = true + ) { const requiredErrorParameters: string[] = []; const validationErrorParameterMap: Record = {}; const result = Object.entries(this.parametersConstruct).reduce< ParsedParameters >((payload, [parameterName, construct]) => { const value = (() => { - const serialized = serializedParameters[parameterName]; + const serialized = stringifiedParameters[parameterName]; const parsedValue = typeof serialized === 'string' ? construct.parse(serialized) @@ -156,13 +159,17 @@ class TypedParameters>> { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return construct.defaultValue; } - if (construct.required) { + if (shouldValidate && construct.required) { requiredErrorParameters.push(parameterName); } return undefined; })(); - if (value != null && typeof construct.validate === 'function') { + if ( + shouldValidate && + value != null && + typeof construct.validate === 'function' + ) { const validationError = construct.validate(value); if (validationError) { if (Array.isArray(validationError)) { @@ -188,23 +195,30 @@ class TypedParameters>> { throw new ParameterError( requiredErrorParameters, validationErrorParameterMap, - serializedParameters, + stringifiedParameters, result ); } return result; } - stringify(parameters: Partial>) { + stringify( + parsedParameters: Partial>, + shouldValidate = true + ) { const requiredErrorParameters: string[] = []; const validationErrorParameterMap: Record = {}; const result = Object.entries(this.parametersConstruct).reduce< StringifiedParameters >((payload, [parameterName, construct]) => { const serialized = (() => { - const value = parameters[parameterName]; + const value = parsedParameters[parameterName]; - if (value != null && typeof construct.validate === 'function') { + if ( + shouldValidate && + value != null && + typeof construct.validate === 'function' + ) { const validationError = construct.validate(value); if (validationError) { if (Array.isArray(validationError)) { @@ -230,7 +244,7 @@ class TypedParameters>> { return serializedDefaultValue; } } - if (construct.required) { + if (shouldValidate && construct.required) { requiredErrorParameters.push(parameterName); } return undefined; @@ -250,7 +264,7 @@ class TypedParameters>> { requiredErrorParameters, validationErrorParameterMap, result, - parameters + parsedParameters ); } return result; diff --git a/test/index.spec.ts b/test/index.spec.ts index b766357..b45662d 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -41,6 +41,10 @@ describe('index', () => { ); }); + it('parse required error but should not validate', () => { + expect(parameters.parse({}, false)).toEqual({}); + }); + it('stringify', () => { expect( parameters.stringify({ @@ -69,6 +73,10 @@ describe('index', () => { 'stringValue is required, unionStringValue is required, numberValue is required, unionNumberValue is required, booleanValue is required, jsonValue is required, arrayValue is required.' ); }); + + it('stringify required error but should not validate', () => { + expect(parameters.stringify({}, false)).toEqual({}); + }); }); describe('all required with defaultValue.', () => { @@ -401,6 +409,33 @@ describe('index', () => { ); }); + it('parse validation error but should not validate', () => { + expect( + parameters.parse( + { + stringValue: 'yyyy', + unionStringValue: 'v3', + numberValue: '0', + unionNumberValue: '-1', + booleanValue: 'false', + jsonValue: '{"apiKey":""}', + arrayValue: '[]', + }, + false + ) + ).toEqual({ + arrayValue: [], + booleanValue: false, + jsonValue: { + apiKey: '', + }, + numberValue: 0, + stringValue: 'yyyy', + unionNumberValue: -1, + unionStringValue: 'v3', + }); + }); + it('parse required error and validation error', () => { expect(() => parameters.parse({ @@ -414,6 +449,25 @@ describe('index', () => { ); }); + it('parse required error and validation error, but should not validaet', () => { + expect( + parameters.parse( + { + stringValue: 'yyyy', + numberValue: '0', + booleanValue: 'false', + arrayValue: '[]', + }, + false + ) + ).toEqual({ + stringValue: 'yyyy', + numberValue: 0, + booleanValue: false, + arrayValue: [], + }); + }); + it('stringify', () => { expect( parameters.stringify({ @@ -463,6 +517,31 @@ describe('index', () => { ); }); + it('stringify validation error but should not validate', () => { + expect( + parameters.stringify( + { + stringValue: 'yyyy', + unionStringValue: 'v3' as never, + numberValue: 0, + unionNumberValue: -1 as never, + booleanValue: false, + jsonValue: { apiKey: '' }, + arrayValue: [], + }, + false + ) + ).toEqual({ + stringValue: 'yyyy', + unionStringValue: 'v3', + numberValue: '0', + unionNumberValue: '-1', + booleanValue: 'false', + jsonValue: '{"apiKey":""}', + arrayValue: '[]', + }); + }); + it('stringify required error and validation error', () => { expect(() => parameters.stringify({ @@ -474,5 +553,22 @@ describe('index', () => { 'stringValue is required, numberValue is required, booleanValue is required, arrayValue is required. unionStringValue: the value must be v1 or v2, unionNumberValue: the value must be 0 or 1, jsonValue: apiKey must be specified.' ); }); + + it('stringify required error and validation error, but should not validate', () => { + expect( + parameters.stringify( + { + unionStringValue: 'v3' as never, + unionNumberValue: -1 as never, + jsonValue: { apiKey: '' }, + }, + false + ) + ).toEqual({ + unionStringValue: 'v3', + unionNumberValue: '-1', + jsonValue: '{"apiKey":""}', + }); + }); }); });