Skip to content

More advanced typescript #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 18 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.2.1-beta.0",
"version": "0.3.0-beta.0",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export { buildQueryString } from './buildQueryString';
export { pickQueryParamsMatchingSchema } from './pickQueryParamsMatchingSchema';

/** Other */
export { QS_BUILD_STRATEGY } from './types';
export { QS_BUILD_STRATEGY, QueryParamsState } from './types';
export { Errors } from './errors';
export { VALIDATORS } from './validators';
99 changes: 99 additions & 0 deletions src/internal/createQueryParamDef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { isUndefined, isFunction } from './typeChecking';

import {
Serializer,
DefaultValue,
DefaultValueFunction,
IQueryParamTypeOptions,
QueryParamValue,
QueryParamOptions,
} from '../types';

export function createQueryParamDef<
T,
QueryParamTypeOptions extends Partial<IQueryParamTypeOptions>
>(
serializer: Serializer<T, QueryParamTypeOptions>,
defaultValue?: DefaultValue<T, QueryParamTypeOptions>,
options?: QueryParamOptions<T, QueryParamTypeOptions>
) {
let validatorFn = options?.validator;

/**
* defaultValue value can be a static value or a function
* calculating the default value
*/
function isDefaultValueFunction(
defaultValue: DefaultValue<T, QueryParamTypeOptions> | undefined
): defaultValue is DefaultValueFunction<
QueryParamValue<T, QueryParamTypeOptions>
> {
return isFunction(defaultValue);
}

/**
* Get the default static value or run defaultValue function to get it.
* @param contextData
*/
function getDefaultValue(
contextData?: any
): QueryParamValue<T, QueryParamTypeOptions> | undefined {
if (isDefaultValueFunction(defaultValue)) {
return defaultValue(contextData);
}

return defaultValue;
}

/**
* Deserialize the query params from string to the defined query param type.
*/
function fromURL(
value: QueryParamValue<string, QueryParamTypeOptions>,
contextData?: any
): QueryParamValue<T, QueryParamTypeOptions> {
const parsedValue = serializer.fromUrl(value);

// Value not found in the URL
if (isUndefined(parsedValue)) {
const defaultValue = getDefaultValue(contextData);
if (isUndefined(defaultValue)) {
if (options?.allowUndefined) {
return undefined as QueryParamValue<T, QueryParamTypeOptions>;
} else {
throw new Error('Missing default value');
}
}
return defaultValue;
}
return parsedValue;
}

/**
* Serialized the query param from the defined query param type to string
*/
function toURL(
value: QueryParamValue<T, QueryParamTypeOptions>
): QueryParamValue<string, QueryParamTypeOptions> {
return serializer.toUrl(value);
}

function runValidator(
value: QueryParamValue<T, QueryParamTypeOptions>,
parsedQueryParams: object,
contextData?: any
): void {
if (validatorFn) {
validatorFn(value, parsedQueryParams, contextData);
}
}

const queryParamDef = {
fromURL,
toURL,
runValidator,
getDefaultValue,
};

return queryParamDef;
}
6 changes: 4 additions & 2 deletions src/internal/getQueryParamsFromURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ export function getRawQueryParamsInSchemaFromURL<
queryParamKey: string
) => {
if (schema.hasOwnProperty(queryParamKey)) {
acc[queryParamKey as keyof QueryParamsSchema] =
allRawQueryParams[queryParamKey];
acc[queryParamKey as keyof QueryParamsSchema] = allRawQueryParams[
queryParamKey
/** TODO: dirty fix, need to be checked properly */
] as any;
}
return acc;
},
Expand Down
84 changes: 0 additions & 84 deletions src/internal/queryParamDef.ts

This file was deleted.

18 changes: 9 additions & 9 deletions src/internal/serializer/serialize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isUndefined } from '../typeChecking';
import {
QueryParamsState,
RawQueryParams,
Expand All @@ -25,10 +24,10 @@ export function deserializeQueryParamsValues<
return Object.keys(queryParamsSchema).reduce(
(acc, queryParamKey: keyof QueryParamsSchema) => {
const queryParamDef = queryParamsSchema[queryParamKey];
acc[queryParamKey] = queryParamDef.fromURL(
rawQueryParams[queryParamKey],
contextData
);
// if (queryParamKey in rawQueryParams) {
const rawValue = rawQueryParams[queryParamKey] as string;
acc[queryParamKey] = queryParamDef.fromURL(rawValue, contextData);
// }

return acc;
},
Expand All @@ -43,7 +42,10 @@ export function serializeQueryParamsValues<
queryParams: Partial<QueryParamsState<QueryParamsSchema>>
): RawQueryParams<QueryParamsSchema> {
return Object.keys(queryParams).reduce(
(acc, queryParamKey: keyof QueryParamsSchema) => {
(
acc: RawQueryParams<QueryParamsSchema>,
queryParamKey: keyof QueryParamsSchema
) => {
const queryParamDef = queryParamsSchema[queryParamKey];
if (!queryParamDef) {
const availableQueryParamsKeys = Object.keys(queryParamsSchema);
Expand All @@ -55,9 +57,7 @@ export function serializeQueryParamsValues<
}
try {
let value = queryParamDef.toURL(queryParams[queryParamKey]);
if (!isUndefined(value)) {
acc[queryParamKey] = value;
}
acc[queryParamKey] = value;
} catch (error) {
// Add query param name information to the error
error.message = `${queryParamKey} ${error.message}`;
Expand Down
10 changes: 5 additions & 5 deletions src/internal/serializer/serializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Errors } from '../../errors';

const ARRAY_DELIMITER = ',';

const arrayStringsSerializer: Serializer<string[]> = {
const arrayStringsSerializer: Serializer<string[], any> = {
fromUrl(str?: string | null): Array<string> | null | undefined {
if (isNil(str)) {
return str;
Expand All @@ -31,7 +31,7 @@ const arrayStringsSerializer: Serializer<string[]> = {
},
};

const arrayNumbersSerializer: Serializer<number[]> = {
const arrayNumbersSerializer: Serializer<number[], any> = {
fromUrl(str?: string | null): Array<number> | null | undefined {
if (isNil(str)) {
return str;
Expand All @@ -58,7 +58,7 @@ const arrayNumbersSerializer: Serializer<number[]> = {
},
};

const stringSerializer: Serializer<string> = {
const stringSerializer: Serializer<string, any> = {
fromUrl: (str?: string | null): string | null | undefined => {
return str;
},
Expand All @@ -77,7 +77,7 @@ const stringSerializer: Serializer<string> = {
},
};

const booleanSerializer: Serializer<boolean> = {
const booleanSerializer: Serializer<boolean, any> = {
fromUrl: (str?: string | null): boolean | null | undefined => {
if (isNil(str)) {
return str;
Expand Down Expand Up @@ -114,7 +114,7 @@ const booleanSerializer: Serializer<boolean> = {
},
};

const numberSerializer: Serializer<number> = {
const numberSerializer: Serializer<number, any> = {
fromUrl: (str?: string | null): number | null | undefined => {
if (isNil(str)) {
return str;
Expand Down
64 changes: 53 additions & 11 deletions src/qparams.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,68 @@
import { DefaultValue } from './types';
import { QueryParamDef } from './internal/queryParamDef';

import { createQueryParamDef } from './internal/createQueryParamDef';

import serializers from './internal/serializer/serializers';
import { IQueryParamTypeOptions, QueryParamOptions, Serializer } from './types';

/**
* Object used to build your query params state schema.
* It contains a factory function for each param type.
*/
export const QPARAMS = {
number: (defaultValue?: DefaultValue<number>) => {
return new QueryParamDef(serializers.NUMBER, defaultValue);
number: <QueryParamTypeOptions extends IQueryParamTypeOptions>(
defaultValue: DefaultValue<number, QueryParamTypeOptions>,
queryParamsOptions?: QueryParamOptions<number, QueryParamTypeOptions>
) => {
const serializer = serializers.NUMBER as Serializer<
number,
QueryParamTypeOptions
>;

return createQueryParamDef(serializer, defaultValue, queryParamsOptions);
},
string: (defaultValue?: DefaultValue<string>) => {
return new QueryParamDef(serializers.STRING, defaultValue);
string: <QueryParamTypeOptions extends IQueryParamTypeOptions>(
defaultValue: DefaultValue<string, QueryParamTypeOptions>,
queryParamsOptions?: QueryParamOptions<string, QueryParamTypeOptions>
) => {
const serializer = serializers.STRING as Serializer<
string,
QueryParamTypeOptions
>;

return createQueryParamDef(serializer, defaultValue, queryParamsOptions);
},
boolean: (defaultValue?: DefaultValue<boolean>) => {
return new QueryParamDef(serializers.BOOLEAN, defaultValue);
boolean: <QueryParamTypeOptions extends IQueryParamTypeOptions>(
defaultValue: DefaultValue<boolean, QueryParamTypeOptions>,
queryParamsOptions?: QueryParamOptions<boolean, QueryParamTypeOptions>
) => {
const serializer = serializers.BOOLEAN as Serializer<
boolean,
QueryParamTypeOptions
>;

return createQueryParamDef(serializer, defaultValue, queryParamsOptions);
},
arrayOfNumbers: (defaultValue?: DefaultValue<number[]>) => {
return new QueryParamDef(serializers.ARRAY__NUMBERS, defaultValue);
arrayOfNumbers: <QueryParamTypeOptions extends IQueryParamTypeOptions>(
defaultValue: DefaultValue<number[], QueryParamTypeOptions>,
queryParamsOptions?: QueryParamOptions<number[], QueryParamTypeOptions>
) => {
const serializer = serializers.ARRAY__NUMBERS as Serializer<
number[],
QueryParamTypeOptions
>;

return createQueryParamDef(serializer, defaultValue, queryParamsOptions);
},
arrayOfStrings: (defaultValue?: DefaultValue<string[]>) => {
return new QueryParamDef(serializers.ARRAY__STRINGS, defaultValue);
arrayOfStrings: <QueryParamTypeOptions extends IQueryParamTypeOptions>(
defaultValue: DefaultValue<string[], QueryParamTypeOptions>,
queryParamsOptions?: QueryParamOptions<string[], QueryParamTypeOptions>
) => {
const serializer = serializers.ARRAY__STRINGS as Serializer<
string[],
QueryParamTypeOptions
>;

return createQueryParamDef(serializer, defaultValue, queryParamsOptions);
},
};
Loading