Skip to content
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

Use interface instead of type for all Base types #259

Merged
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fd86987
Use `interface` instead of `type` for all Base types
Saeris Nov 19, 2023
990140a
Refactor Fallback and Default methods, Add Schema Type Predicates
Saeris Nov 21, 2023
cc38e4f
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Nov 21, 2023
5b483f4
Fix file extensions lint errors
Saeris Nov 22, 2023
a6bbcd1
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Nov 22, 2023
a1584f1
Add jsdoc `@param` and `@returns` required by linting rules
Saeris Nov 22, 2023
c08f732
Resolve Flatten type errors
Saeris Nov 22, 2023
7f84338
Fix broken export for `intersectAsync`
Saeris Nov 22, 2023
cab5616
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Dec 5, 2023
a89b486
Fix eslint error
Saeris Dec 5, 2023
3b2d314
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Dec 5, 2023
1b516da
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Jan 6, 2024
5078f72
Resolve lint errors (no-extra-semi)
Saeris Jan 6, 2024
27dec16
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Jan 8, 2024
6949d2d
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Jan 18, 2024
c2dc365
Fix extra semi errors from merge
Saeris Jan 18, 2024
527a05c
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Jan 18, 2024
e5c79ca
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Jan 19, 2024
0a426bb
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Jan 21, 2024
e195488
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Jan 24, 2024
0676aff
Fix merge errors
Saeris Jan 24, 2024
0604c26
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Feb 5, 2024
bb424a0
Fix formatting
Saeris Feb 5, 2024
b6a2fb2
Merge branch 'main' into feat/use-interface-for-base-types
fabian-hiller Feb 17, 2024
0a5889a
Fix types and exports of fallback method
fabian-hiller Feb 17, 2024
fe36ae1
Add `isOfType` utility and refactor type guard implementations
Saeris Feb 27, 2024
9574d5d
Merge branch 'feat/use-interface-for-base-types' of https://github.co…
Saeris Feb 27, 2024
2a28529
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Mar 4, 2024
48ce458
Merge branch 'main' into feat/use-interface-for-base-types
Saeris Mar 4, 2024
02b17d5
run prettier and fix lint warnings
Saeris Mar 4, 2024
2cefacc
Add Default and DefaultAsync type and refactor codebase
fabian-hiller Mar 6, 2024
114b1f8
Add Fallback and FallbackAsync type and refactor codebase
fabian-hiller Mar 6, 2024
e08af7f
Refactor getDefaults and getFallbacks method
fabian-hiller Mar 6, 2024
03e95e3
Undo changes of NestedPath type in flatten
fabian-hiller Mar 6, 2024
7e79352
Rename hasType to isOfType and refactor codebase
fabian-hiller Mar 6, 2024
2f77fc2
Add Default and Fallback to API reference of website
fabian-hiller Mar 6, 2024
de1d857
Update changelog of library
fabian-hiller Mar 6, 2024
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
107 changes: 48 additions & 59 deletions library/src/error/flatten/flatten.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,67 +70,56 @@ type NestedPath<TSchema extends BaseSchema | BaseSchemaAsync> =
>
? DotPath<number, TItem>
: // Map
TSchema extends MapSchema<infer TKey, infer TValue>
TSchema extends
| MapSchema<infer TKey, infer TValue>
| MapSchemaAsync<infer TKey, infer TValue>
? DotPath<Input<TKey>, TValue>
: TSchema extends MapSchemaAsync<infer TKey, infer TValue>
? DotPath<Input<TKey>, TValue>
: // Object
TSchema extends ObjectSchema<infer TEntries, infer TRest>
? TRest extends BaseSchema
? ObjectPath<TEntries> | DotPath<string, TRest>
: ObjectPath<TEntries>
: TSchema extends ObjectSchemaAsync<infer TEntries, infer TRest>
? TRest extends BaseSchema | BaseSchemaAsync
? ObjectPath<TEntries> | DotPath<string, TRest>
: ObjectPath<TEntries>
: // Record
TSchema extends RecordSchema<infer TKey, infer TValue>
? DotPath<Input<TKey>, TValue>
: TSchema extends RecordSchemaAsync<infer TKey, infer TValue>
? DotPath<Input<TKey>, TValue>
: // Lazy
TSchema extends LazySchema<
infer TSchemaGetter extends () => BaseSchema
>
? NestedPath<ReturnType<TSchemaGetter>>
: TSchema extends LazySchemaAsync<
infer TSchemaGetter extends () =>
| BaseSchema
| BaseSchemaAsync
: // Object
TSchema extends
| ObjectSchema<infer TEntries, infer TRest>
| ObjectSchemaAsync<infer TEntries, infer TRest>
? TRest extends NonNullable<TRest>
? ObjectPath<TEntries> | DotPath<string, TRest>
: ObjectPath<TEntries>
: // Record
TSchema extends
| RecordSchema<infer TKey, infer TValue>
| RecordSchemaAsync<infer TKey, infer TValue>
? DotPath<Input<TKey>, TValue>
: // Lazy
TSchema extends LazySchema<
infer TSchemaGetter extends () => BaseSchema
>
? NestedPath<ReturnType<TSchemaGetter>>
: TSchema extends LazySchemaAsync<
infer TSchemaGetter extends () =>
| BaseSchema
| BaseSchemaAsync
>
? NestedPath<ReturnType<TSchemaGetter>>
: // Set
TSchema extends
| SetSchema<infer TValue>
| SetSchemaAsync<infer TValue>
? DotPath<number, TValue>
: // Tuple
TSchema extends
| TupleSchema<infer TItems, infer TRest>
| TupleSchemaAsync<infer TItems, infer TRest>
? TRest extends NonNullable<TRest>
? TuplePath<TItems> | DotPath<number, TRest>
: TuplePath<TItems>
: // Union
TSchema extends UnionSchema<
infer TUnionOptions extends UnionOptions
>
? NestedPath<ReturnType<TSchemaGetter>>
: // Set
TSchema extends SetSchema<infer TValue>
? DotPath<number, TValue>
: TSchema extends SetSchemaAsync<infer TValue>
? DotPath<number, TValue>
: // Tuple
TSchema extends TupleSchema<
infer TItems,
infer TRest
>
? TRest extends BaseSchema
? TuplePath<TItems> | DotPath<number, TRest>
: TuplePath<TItems>
: TSchema extends TupleSchemaAsync<
infer TItems,
infer TRest
>
? TRest extends BaseSchema | BaseSchemaAsync
? TuplePath<TItems> | DotPath<number, TRest>
: TuplePath<TItems>
: // Union
TSchema extends UnionSchema<
infer TUnionOptions extends UnionOptions
>
? NestedPath<TUnionOptions[number]>
: TSchema extends UnionSchemaAsync<
infer TUnionOptions extends
UnionOptionsAsync
>
? NestedPath<TUnionOptions[number]>
: // Otherwise
never;
? NestedPath<TUnionOptions[number]>
: TSchema extends UnionSchemaAsync<
infer TUnionOptions extends UnionOptionsAsync
>
? NestedPath<TUnionOptions[number]>
: // Otherwise
never;

/**
* Flat errors type.
Expand Down
12 changes: 4 additions & 8 deletions library/src/methods/fallback/fallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,13 @@ import type { FallbackInfo } from './types.ts';
/**
* Schema with fallback type.
*/
export type SchemaWithFallback<
TSchema extends BaseSchema = BaseSchema,
TFallback extends
| Output<TSchema>
| ((info?: FallbackInfo) => Output<TSchema>) = Output<TSchema>,
> = TSchema & {
export interface SchemaWithFallback<TInput = any, TOutput = TInput>
extends BaseSchema<TInput, TOutput> {
/**
* The fallback value.
*/
fallback: TFallback;
};
fallback: TOutput | ((info?: FallbackInfo) => TOutput);
}

/**
* Returns a fallback output value when validating the passed schema failed.
Expand Down
14 changes: 4 additions & 10 deletions library/src/methods/fallback/fallbackAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ import type { FallbackInfo } from './types.ts';
/**
* Schema with fallback async type.
*/
export type SchemaWithFallbackAsync<
TSchema extends BaseSchemaAsync = BaseSchemaAsync,
TFallback extends
| Output<TSchema>
| ((
info?: FallbackInfo
) => Output<TSchema> | Promise<Output<TSchema>>) = Output<TSchema>,
> = TSchema & {
export interface SchemaWithFallbackAsync<TInput = any, TOutput = TInput>
extends BaseSchemaAsync<TInput, TOutput> {
/**
* The fallback value.
*/
fallback: TFallback;
};
fallback: TOutput | ((info?: FallbackInfo) => TOutput | Promise<TOutput>);
}

/**
* Returns a fallback output value when validating the passed schema failed.
Expand Down
14 changes: 1 addition & 13 deletions library/src/methods/getDefault/getDefault.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
import type { BaseSchema, Output } from '../../types/index.ts';
import type { DefaultValue } from './types.ts';

/**
* Schema with maybe default type.
*/
export type SchemaWithMaybeDefault<TSchema extends BaseSchema = BaseSchema> =
TSchema & {
/**
* The optional default value.
*/
default?: Output<TSchema> | (() => Output<TSchema> | undefined);
};
import type { DefaultValue, SchemaWithMaybeDefault } from './types.ts';

/**
* Returns the default value of the schema.
Expand Down
25 changes: 5 additions & 20 deletions library/src/methods/getDefault/getDefaultAsync.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,8 @@
import type { BaseSchemaAsync, Output } from '../../types/index.ts';
import type { SchemaWithMaybeDefault } from './getDefault.ts';
import type { DefaultValue } from './types.ts';

/**
* Schema with maybe default async type.
*/
export type SchemaWithMaybeDefaultAsync<
TSchema extends BaseSchemaAsync = BaseSchemaAsync,
> = TSchema & {
/**
* The optional default value.
*/
default?:
| Output<TSchema>
| (() =>
| Output<TSchema>
| Promise<Output<TSchema> | undefined>
| undefined);
};
import type {
DefaultValue,
SchemaWithMaybeDefault,
SchemaWithMaybeDefaultAsync,
} from './types.ts';

/**
* Returns the default value of the schema.
Expand Down
28 changes: 25 additions & 3 deletions library/src/methods/getDefault/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
import type { Output } from '../../types/index.ts';
import type { SchemaWithMaybeDefault } from './getDefault.ts';
import type { SchemaWithMaybeDefaultAsync } from './getDefaultAsync.ts';
import type { BaseSchema, BaseSchemaAsync, Output } from '../../types/index.ts';

/**
* Schema with maybe default type.
*/
export interface SchemaWithMaybeDefault<TInput = any, TOutput = TInput>
extends BaseSchema<TInput, TOutput> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this into getDefaults.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved these types because they created a circular dependency reference. IMO they are better kept in this file.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it create a circular dependency reference? I prefer to move it to getDefaults.ts to be consistent with the rest of the source code. Types that are related to a particular function are placed above the function in the same file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because for example:

export type DefaultValue<
  TSchema extends SchemaWithMaybeDefault | SchemaWithMaybeDefaultAsync,
>

in types.ts would import from getDefaults.ts, which itself imports DefaultValue from types,ts

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will have a look at it. I don't think this is a problem but I could be wrong.

/**
* The optional default value.
*/
default?: TOutput | (() => TOutput | undefined);
}

/**
* Schema with maybe default async type.
*/
export interface SchemaWithMaybeDefaultAsync<TInput = any, TOutput = TInput>
extends BaseSchemaAsync<TInput, TOutput> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this into getDefaultsAsync.ts

/**
* The optional default value.
*/
default?:
| TOutput
| (() => TOutput | Promise<TOutput | undefined> | undefined);
}

/**
* Default value inference type.
Expand Down
49 changes: 20 additions & 29 deletions library/src/methods/getDefaults/getDefaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import type {
TupleItems,
TupleSchema,
} from '../../schemas/index.ts';
import type { BaseSchema } from '../../types/index.ts';
import type { BaseSchema } from '../../types/schema.ts';
import { hasType } from '../../utils/index.ts';
import {
getDefault,
type SchemaWithMaybeDefault,
Expand All @@ -23,35 +24,25 @@ import type { DefaultValues } from './types.ts';
* @returns The default values.
*/
export function getDefaults<
TSchema extends SchemaWithMaybeDefault<
BaseSchema | ObjectSchema<ObjectEntries, any> | TupleSchema<TupleItems, any>
>,
>(schema: TSchema): DefaultValues<TSchema> {
// Create defaults variable
let defaults: any;

TSchema extends SchemaWithMaybeDefault &
(BaseSchema | ObjectSchema<ObjectEntries> | TupleSchema<TupleItems>),
>(schema: TSchema): DefaultValues<TSchema> | undefined {
Copy link

@aboqasem aboqasem Mar 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, as per the documentation for getDefaults:

The difference to getDefault is that for objects and tuples without an explicit default value, this function recursively returns the default values of the subschemas instead of undefined.

Did that change?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it should work just the same. All unit tests passed, so I do not expect any bugs, but maybe we are missing something. Please create an issue if you encounter any problems.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok thanks. It just broke the type when I upgraded so I was wondering.

Copy link
Owner

@fabian-hiller fabian-hiller Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to share your code if you thing something is wrong on our side.

// If schema contains a default function, set its default value
if (schema.default !== undefined) {
defaults = getDefault(schema);

// Otherwise, check if schema is of kind object or tuple
} else if ('type' in schema) {
// If it is an object schema, set object with default value of each entry
if (schema.type === 'object') {
defaults = {};
for (const key in schema.entries) {
defaults[key] = getDefaults(schema.entries[key]);
}

// If it is a tuple schema, set array with default value of each item
} else if (schema.type === 'tuple') {
defaults = [];
for (let key = 0; key < schema.items.length; key++) {
defaults.push(getDefaults(schema.items[key]));
}
}
return getDefault(schema);
}
// Otherwise, check if schema is of kind object or tuple
// If it is an object schema, set object with default value of each entry
if (hasType(schema, 'object')) {
return Object.fromEntries(
Object.entries(schema.entries).map(([key, value]) => [
key,
getDefaults(value),
])
) as DefaultValues<TSchema>;
}
// If it is a tuple schema, set array with default value of each item
if (hasType(schema, 'tuple')) {
return schema.items.map(getDefaults) as DefaultValues<TSchema>;
}

// Return default values
return defaults;
}
66 changes: 30 additions & 36 deletions library/src/methods/getDefaults/getDefaultsAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import type {
TupleSchema,
TupleSchemaAsync,
} from '../../schemas/index.ts';
import type { BaseSchema, BaseSchemaAsync } from '../../types/index.ts';
import type { BaseSchema, BaseSchemaAsync } from '../../types/schema.ts';
import { hasType } from '../../utils/index.ts';
import {
getDefaultAsync,
type SchemaWithMaybeDefault,
Expand All @@ -29,42 +30,35 @@ import type { DefaultValues } from './types.ts';
*/
export async function getDefaultsAsync<
TSchema extends
| SchemaWithMaybeDefault<
| BaseSchema
| ObjectSchema<ObjectEntries, any>
| TupleSchema<TupleItems, any>
>
| SchemaWithMaybeDefaultAsync<
| BaseSchemaAsync
| ObjectSchemaAsync<ObjectEntriesAsync, any>
| TupleSchemaAsync<TupleItemsAsync, any>
>,
>(schema: TSchema): Promise<DefaultValues<TSchema>> {
// Create defaults variable
let defaults: any;

| (SchemaWithMaybeDefault &
(BaseSchema | ObjectSchema<ObjectEntries> | TupleSchema<TupleItems>))
| (SchemaWithMaybeDefaultAsync &
(
| BaseSchemaAsync
| ObjectSchemaAsync<ObjectEntriesAsync>
| TupleSchemaAsync<TupleItemsAsync>
)),
>(schema: TSchema): Promise<DefaultValues<TSchema> | undefined> {
// If schema contains a default function, set its default value
if (schema.default !== undefined) {
defaults = await getDefaultAsync(schema);

// Otherwise, check if schema is of kind object or tuple
} else if ('type' in schema) {
// If it is an object schema, set object with default value of each entry
if (schema.type === 'object') {
defaults = {};
for (const key in schema.entries) {
defaults[key] = await getDefaultsAsync(schema.entries[key]);
}

// If it is a tuple schema, set array with default value of each item
} else if (schema.type === 'tuple') {
defaults = [];
for (let key = 0; key < schema.items.length; key++) {
defaults.push(await getDefaultsAsync(schema.items[key]));
}
}
return getDefaultAsync(schema);
}
// Otherwise, check if schema is of kind object or tuple
// If it is an object schema, set object with default value of each entry
if (hasType(schema, 'object')) {
return Object.fromEntries(
await Promise.all(
Object.entries(schema.entries).map(async ([key, value]) => [
key,
await getDefaultsAsync(value),
])
)
);
}
// If it is a tuple schema, set array with default value of each item
if (hasType(schema, 'tuple')) {
return Promise.all(
schema.items.map(getDefaultsAsync)
) as DefaultValues<TSchema>;
}

// Return default values
return defaults;
}
Loading
Loading