Skip to content

Commit

Permalink
feat: simplify base class hierarchy (#1543)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `mixed` schema are no longer treated as the base class for other schema types. It hasn't been for a while, but we've done some nasty prototype slinging to make it behave like it was. Now typescript types should be 1 to 1 with the actual classes yup exposes. 

In general this should not affect anything unless you are extending (via `addMethod` or otherwise) `mixed` prototype. 

```diff
import {
-  mixed,
+  Schema,
} from 'yup';

- addMethod(mixed, 'method', impl)
+ addMethod(Schema, 'method', impl)
```
  • Loading branch information
jquense authored Dec 29, 2021
1 parent 3923039 commit c184dcf
Show file tree
Hide file tree
Showing 14 changed files with 404 additions and 301 deletions.
518 changes: 315 additions & 203 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Note that `addMethod` isn't magic, it mutates the prototype of the passed in sch

If you're use case calls for creating an entirely new type. inheriting from
and existing schema class may be best: Generally you should not inheriting from
the abstract `BaseSchema` unless you know what you are doing. The other types are fair game though.
the abstract `Schema` unless you know what you are doing. The other types are fair game though.

You should keep in mind some basic guidelines when extending schemas:

Expand Down
6 changes: 3 additions & 3 deletions src/Lazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
SchemaLazyDescription,
} from './schema';
import { Flags, ISchema } from './util/types';
import { BaseSchema } from '.';
import { Schema } from '.';

export type LazyBuilder<
T,
Expand Down Expand Up @@ -60,8 +60,8 @@ class Lazy<T, TContext = AnyObject, TDefault = any, TFlags extends Flags = any>
private _resolve = (
value: any,
options: ResolveOptions<TContext> = {},
): BaseSchema<T, TContext, TDefault, TFlags> => {
let schema = this.builder(value, options) as BaseSchema<
): Schema<T, TContext, TDefault, TFlags> => {
let schema = this.builder(value, options) as Schema<
T,
TContext,
TDefault,
Expand Down
6 changes: 3 additions & 3 deletions src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
ISchema,
UnsetFlag,
} from './util/types';
import BaseSchema, { SchemaInnerTypeDescription, SchemaSpec } from './schema';
import Schema, { SchemaInnerTypeDescription, SchemaSpec } from './schema';
import { ResolveOptions } from './Condition';

export type RejectorFn = (
Expand All @@ -43,7 +43,7 @@ export default class ArraySchema<
TDefault = undefined,
TFlags extends Flags = '',
TIn extends any[] | null | undefined = T[] | undefined,
> extends BaseSchema<TIn, TContext, TDefault, TFlags> {
> extends Schema<TIn, TContext, TDefault, TFlags> {
innerType?: ISchema<T, TContext>;

constructor(type?: ISchema<T, TContext>) {
Expand Down Expand Up @@ -289,7 +289,7 @@ export default interface ArraySchema<
TDefault = undefined,
TFlags extends Flags = '',
TIn extends any[] | null | undefined = T[] | undefined,
> extends BaseSchema<TIn, TContext, TDefault, TFlags> {
> extends Schema<TIn, TContext, TDefault, TFlags> {
default<D extends Maybe<TIn>>(
def: Thunk<D>,
): ArraySchema<T, TContext, D, ToggleDefault<TFlags, D>, TIn>;
Expand Down
4 changes: 2 additions & 2 deletions src/boolean.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import BaseSchema from './schema';
import Schema from './schema';
import type { AnyObject, Maybe, Message, Optionals } from './types';
import type {
Defined,
Expand Down Expand Up @@ -26,7 +26,7 @@ export default class BooleanSchema<
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = '',
> extends BaseSchema<TType, TContext, TDefault, TFlags> {
> extends Schema<TType, TContext, TDefault, TFlags> {
constructor() {
super({
type: 'boolean',
Expand Down
6 changes: 3 additions & 3 deletions src/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
ToggleDefault,
UnsetFlag,
} from './util/types';
import BaseSchema from './schema';
import Schema from './schema';

let invalidDate = new Date('');

Expand All @@ -34,7 +34,7 @@ export default class DateSchema<
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = '',
> extends BaseSchema<TType, TContext, TDefault, TFlags> {
> extends Schema<TType, TContext, TDefault, TFlags> {
static INVALID_DATE = invalidDate;

constructor() {
Expand Down Expand Up @@ -113,7 +113,7 @@ export default interface DateSchema<
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = '',
> extends BaseSchema<TType, TContext, TDefault, TFlags> {
> extends Schema<TType, TContext, TDefault, TFlags> {
default<D extends Maybe<TType>>(
def: Thunk<D>,
): DateSchema<TType, TContext, D, ToggleDefault<TFlags, D>>;
Expand Down
12 changes: 6 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Mixed, {
import MixedSchema, {
create as mixedCreate,
MixedSchema,
MixedOptions,
TypeGuard,
} from './mixed';
import BooleanSchema, { create as boolCreate } from './boolean';
import StringSchema, { create as stringCreate } from './string';
Expand All @@ -15,7 +15,7 @@ import ValidationError from './ValidationError';
import reach from './util/reach';
import isSchema from './util/isSchema';
import setLocale from './setLocale';
import BaseSchema, { AnySchema } from './schema';
import Schema, { AnySchema } from './schema';
import type { InferType } from './util/types';

function addMethod<T extends AnySchema>(
Expand Down Expand Up @@ -48,6 +48,7 @@ export type {
InferType as Asserts,
AnySchema,
MixedOptions,
TypeGuard,
};

export {
Expand All @@ -69,9 +70,8 @@ export {
};

export {
BaseSchema,
Mixed as MixedSchema,
MixedSchema as MixedSchemaClass,
Schema,
MixedSchema,
BooleanSchema,
StringSchema,
NumberSchema,
Expand Down
55 changes: 33 additions & 22 deletions src/mixed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,42 @@ import type {
ToggleDefault,
UnsetFlag,
} from './util/types';
import BaseSchema from './schema';
import Schema from './schema';

export declare class MixedSchema<
const returnsTrue: any = () => true;

export type TypeGuard<TType> = (value: any) => value is NonNullable<TType>;
export interface MixedOptions<TType> {
type?: string;
check?: TypeGuard<TType>;
}
export function create<TType = any>(
spec?: MixedOptions<TType> | TypeGuard<TType>,
) {
return new MixedSchema<TType | undefined>(spec);
}

export default class MixedSchema<
TType = any,
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = '',
> extends Schema<TType, TContext, TDefault, TFlags> {
constructor(spec?: MixedOptions<TType> | TypeGuard<TType>) {
super(
typeof spec === 'function'
? { type: 'mixed', check: spec }
: { type: 'mixed', check: returnsTrue as TypeGuard<TType>, ...spec },
);
}
}

export default interface MixedSchema<
TType = any,
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = '',
> extends BaseSchema<TType, TContext, TDefault, TFlags> {
> extends Schema<TType, TContext, TDefault, TFlags> {
default<D extends Maybe<TType>>(
def: Thunk<D>,
): MixedSchema<TType, TContext, D, ToggleDefault<TFlags, D>>;
Expand All @@ -24,7 +52,7 @@ export declare class MixedSchema<
schema: MixedSchema<IT, IC, ID, IF>,
): MixedSchema<Concat<TType, IT>, TContext & IC, ID, TFlags | IF>;
concat<IT, IC, ID, IF extends Flags>(
schema: BaseSchema<IT, IC, ID, IF>,
schema: Schema<IT, IC, ID, IF>,
): MixedSchema<Concat<TType, IT>, TContext & IC, ID, TFlags | IF>;
concat(schema: this): this;

Expand Down Expand Up @@ -52,21 +80,4 @@ export declare class MixedSchema<
): MixedSchema<TType, TContext, TDefault, SetFlag<TFlags, 's'>>;
}

const Mixed: typeof MixedSchema = BaseSchema as any;

export default Mixed;

export type TypeGuard<TType> = (value: any) => value is NonNullable<TType>;
export interface MixedOptions<TType> {
type?: string;
check?: TypeGuard<TType>;
}
export function create<TType = any>(
spec?: MixedOptions<TType> | TypeGuard<TType>,
) {
return new Mixed<TType | undefined>(
typeof spec === 'function' ? { check: spec } : spec,
);
}
// XXX: this is using the Base schema so that `addMethod(mixed)` works as a base class
create.prototype = Mixed.prototype;
create.prototype = MixedSchema.prototype;
6 changes: 3 additions & 3 deletions src/number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
ToggleDefault,
UnsetFlag,
} from './util/types';
import BaseSchema from './schema';
import Schema from './schema';

let isNaN = (value: Maybe<number>) => value != +value!;

Expand All @@ -30,7 +30,7 @@ export default class NumberSchema<
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = '',
> extends BaseSchema<TType, TContext, TDefault, TFlags> {
> extends Schema<TType, TContext, TDefault, TFlags> {
constructor() {
super({
type: 'number',
Expand Down Expand Up @@ -156,7 +156,7 @@ export default interface NumberSchema<
TContext = AnyObject,
TDefault = undefined,
TFlags extends Flags = '',
> extends BaseSchema<TType, TContext, TDefault, TFlags> {
> extends Schema<TType, TContext, TDefault, TFlags> {
default<D extends Maybe<TType>>(
def: Thunk<D>,
): NumberSchema<TType, TContext, D, ToggleDefault<TFlags, D>>;
Expand Down
14 changes: 6 additions & 8 deletions src/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { InternalOptions, Callback, Maybe, Message } from './types';
import ValidationError from './ValidationError';
import type { Defined, Thunk, NotNull, _ } from './util/types';
import type Reference from './Reference';
import BaseSchema, { SchemaObjectDescription, SchemaSpec } from './schema';
import Schema, { SchemaObjectDescription, SchemaSpec } from './schema';
import { ResolveOptions } from './Condition';
import type {
AnyObject,
Expand Down Expand Up @@ -74,7 +74,7 @@ export default interface ObjectSchema<
// will match object schema regardless of defaults
TDefault = any,
TFlags extends Flags = '',
> extends BaseSchema<MakeKeysOptional<TIn>, TContext, TDefault, TFlags> {
> extends Schema<MakeKeysOptional<TIn>, TContext, TDefault, TFlags> {
default<D extends Maybe<AnyObject>>(
def: Thunk<D>,
): ObjectSchema<TIn, TContext, D, ToggleDefault<TFlags, 'd'>>;
Expand Down Expand Up @@ -105,7 +105,7 @@ export default class ObjectSchema<
TContext = AnyObject,
TDefault = any,
TFlags extends Flags = '',
> extends BaseSchema<MakeKeysOptional<TIn>, TContext, TDefault, TFlags> {
> extends Schema<MakeKeysOptional<TIn>, TContext, TDefault, TFlags> {
fields: Shape<NonNullable<TIn>, TContext> = Object.create(null);

declare spec: ObjectSchemaSpec;
Expand Down Expand Up @@ -184,7 +184,7 @@ export default class ObjectSchema<
parent: intermediateValue,
});

let fieldSpec = field instanceof BaseSchema ? field.spec : undefined;
let fieldSpec = field instanceof Schema ? field.spec : undefined;
let strict = fieldSpec?.strict;

if (fieldSpec?.strip) {
Expand Down Expand Up @@ -391,7 +391,7 @@ export default class ObjectSchema<
partial() {
const partial: any = {};
for (const [key, schema] of Object.entries(this.fields)) {
partial[key] = schema instanceof BaseSchema ? schema.optional() : schema;
partial[key] = schema instanceof Schema ? schema.optional() : schema;
}

return this.setFields<Partial<TIn>, TDefault>(partial);
Expand All @@ -401,9 +401,7 @@ export default class ObjectSchema<
const partial: any = {};
for (const [key, schema] of Object.entries(this.fields)) {
if (schema instanceof ObjectSchema) partial[key] = schema.deepPartial();
else
partial[key] =
schema instanceof BaseSchema ? schema.optional() : schema;
else partial[key] = schema instanceof Schema ? schema.optional() : schema;
}
return this.setFields<PartialDeep<TIn>, TDefault>(partial);
}
Expand Down
Loading

0 comments on commit c184dcf

Please sign in to comment.