Skip to content

Commit

Permalink
feat: Make Array generic consistent with others
Browse files Browse the repository at this point in the history
BREAKING CHANGE: types only, `ArraySchema`  initial generic is the array type not the type of the array element. `array<T>()` is still the inner type.
  • Loading branch information
jquense committed Dec 29, 2021
1 parent 94b73c4 commit a82353f
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 42 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,17 @@ import {
- [`date.min(limit: Date | string | Ref, message?: string | function): Schema`](#dateminlimit-date--string--ref-message-string--function-schema)
- [`date.max(limit: Date | string | Ref, message?: string | function): Schema`](#datemaxlimit-date--string--ref-message-string--function-schema)
- [array](#array)
- [`array.of(type: Schema): Schema`](#arrayoftype-schema-schema)
- [`array.length(length: number | Ref, message?: string | function): Schema`](#arraylengthlength-number--ref-message-string--function-schema)
- [`array.min(limit: number | Ref, message?: string | function): Schema`](#arrayminlimit-number--ref-message-string--function-schema)
- [`array.max(limit: number | Ref, message?: string | function): Schema`](#arraymaxlimit-number--ref-message-string--function-schema)
- [`array.ensure(): Schema`](#arrayensure-schema)
- [`array.of(type: Schema): this`](#arrayoftype-schema-this)
- [`array.json(): this`](#arrayjson-this)
- [`array.length(length: number | Ref, message?: string | function): this`](#arraylengthlength-number--ref-message-string--function-this)
- [`array.min(limit: number | Ref, message?: string | function): this`](#arrayminlimit-number--ref-message-string--function-this)
- [`array.max(limit: number | Ref, message?: string | function): this`](#arraymaxlimit-number--ref-message-string--function-this)
- [`array.ensure(): this`](#arrayensure-this)
- [`array.compact(rejector: (value) => boolean): Schema`](#arraycompactrejector-value--boolean-schema)
- [object](#object)
- [Object schema defaults](#object-schema-defaults)
- [`object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema`](#objectshapefields-object-nosortedges-arraystring-string-schema)
- [`object.json(): this`](#objectjson-this)
- [`object.concat(schemaB: ObjectSchema): ObjectSchema`](#objectconcatschemab-objectschema-objectschema)
- [`object.pick(keys: string[]): Schema`](#objectpickkeys-string-schema)
- [`object.omit(keys: string[]): Schema`](#objectomitkeys-string-schema)
Expand Down
69 changes: 33 additions & 36 deletions src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
Callback,
Message,
Maybe,
Optionals,
} from './types';
import ValidationError from './ValidationError';
import type Reference from './Reference';
Expand All @@ -27,6 +28,8 @@ import Schema, { SchemaInnerTypeDescription, SchemaSpec } from './schema';
import { ResolveOptions } from './Condition';
import parseJson from 'parse-json';

type InnerType<T> = T extends Array<infer I> ? I : never;

export type RejectorFn = (
value: any,
index: number,
Expand All @@ -36,19 +39,18 @@ export type RejectorFn = (
export function create<C extends AnyObject = AnyObject, T = any>(
type?: ISchema<T, C>,
) {
return new ArraySchema<T, C>(type as any);
return new ArraySchema<T[] | undefined, C>(type as any);
}

export default class ArraySchema<
T,
TIn extends any[] | null | undefined,
TContext,
TDefault = undefined,
TFlags extends Flags = '',
TIn extends any[] | null | undefined = T[] | undefined,
> extends Schema<TIn, TContext, TDefault, TFlags> {
innerType?: ISchema<T, TContext>;
readonly innerType?: ISchema<InnerType<TIn>, TContext>;

constructor(type?: ISchema<T, TContext>) {
constructor(type?: ISchema<InnerType<TIn>, TContext>) {
super({
type: 'array',
check(v: any): v is NonNullable<TIn> {
Expand All @@ -60,15 +62,13 @@ export default class ArraySchema<
this.innerType = type;
}

private get _subType() {
return this.innerType;
}

protected _cast(_value: any, _opts: InternalOptions<TContext>) {
const value = super._cast(_value, _opts);

//should ignore nulls here
if (!this._typeCheck(value) || !this.innerType) return value;
// should ignore nulls here
if (!this._typeCheck(value) || !this.innerType) {
return value;
}

let isChanged = false;
const castArray = value.map((v, idx) => {
Expand Down Expand Up @@ -151,6 +151,7 @@ export default class ArraySchema<

clone(spec?: SchemaSpec<any>) {
const next = super.clone(spec);
// @ts-expect-error readonly
next.innerType = this.innerType;
return next;
}
Expand All @@ -160,22 +161,23 @@ export default class ArraySchema<
return this.transform(parseJson);
}

concat<IT, IC, ID, IF extends Flags, IIn extends Maybe<IT[]>>(
schema: ArraySchema<IT, IC, ID, IF, IIn>,
concat<IIn extends Maybe<any[]>, IC, ID, IF extends Flags>(
schema: ArraySchema<IIn, IC, ID, IF>,
): ArraySchema<
Concat<T, IT>,
Concat<TIn, IIn>,
TContext & IC,
Extract<IF, 'd'> extends never ? TDefault : ID,
TFlags | IF,
Concat<TIn, IIn>
TFlags | IF
>;
concat(schema: this): this;
concat(schema: any): any {
let next = super.concat(schema) as this;

// @ts-expect-error readonly
next.innerType = this.innerType;

if (schema.innerType)
// @ts-expect-error readonly
next.innerType = next.innerType
? // @ts-expect-error Lazy doesn't have concat and will break
next.innerType.concat(schema.innerType)
Expand All @@ -184,7 +186,9 @@ export default class ArraySchema<
return next;
}

of<U>(schema: ISchema<U, TContext>): ArraySchema<U, TContext, TFlags> {
of<U>(
schema: ISchema<U, TContext>,
): ArraySchema<U[] | Optionals<TIn>, TContext, TFlags> {
// FIXME: this should return a new instance of array without the default to be
let next = this.clone();

Expand All @@ -194,8 +198,8 @@ export default class ArraySchema<
printValue(schema),
);

// FIXME(ts):
next.innerType = schema as any;
// @ts-expect-error readonly
next.innerType = schema;

return next as any;
}
Expand Down Expand Up @@ -243,8 +247,6 @@ export default class ArraySchema<
});
}

// ArraySchema<T, TContext, T[], SetFlag<TFlags, 'd'>, NonNullable<TIn>>

ensure() {
return this.default<TIn>(() => [] as any).transform(
(val: TIn, original: any) => {
Expand Down Expand Up @@ -285,35 +287,30 @@ export default class ArraySchema<
create.prototype = ArraySchema.prototype;

export default interface ArraySchema<
T,
TIn extends any[] | null | undefined,
TContext,
TDefault = undefined,
TFlags extends Flags = '',
TIn extends any[] | null | undefined = T[] | undefined,
> extends Schema<TIn, TContext, TDefault, TFlags> {
default<D extends Maybe<TIn>>(
def: Thunk<D>,
): ArraySchema<T, TContext, D, ToggleDefault<TFlags, D>, TIn>;
): ArraySchema<TIn, TContext, D, ToggleDefault<TFlags, D>>;

defined(
msg?: Message,
): ArraySchema<T, TContext, TDefault, TFlags, Defined<TIn>>;
optional(): ArraySchema<T, TContext, TDefault, TFlags, TIn | undefined>;
defined(msg?: Message): ArraySchema<Defined<TIn>, TContext, TDefault, TFlags>;
optional(): ArraySchema<TIn | undefined, TContext, TDefault, TFlags>;

required(
msg?: Message,
): ArraySchema<T, TContext, TDefault, TFlags, NonNullable<TIn>>;
notRequired(): ArraySchema<T, TContext, TDefault, TFlags, Maybe<TIn>>;
): ArraySchema<NonNullable<TIn>, TContext, TDefault, TFlags>;
notRequired(): ArraySchema<Maybe<TIn>, TContext, TDefault, TFlags>;

nullable(
msg?: Message,
): ArraySchema<T, TContext, TDefault, TFlags, TIn | null>;
nonNullable(): ArraySchema<T, TContext, TDefault, TFlags, NotNull<TIn>>;
nullable(msg?: Message): ArraySchema<TIn | null, TContext, TDefault, TFlags>;
nonNullable(): ArraySchema<NotNull<TIn>, TContext, TDefault, TFlags>;

strip(
enabled: false,
): ArraySchema<T, TContext, TDefault, UnsetFlag<TFlags, 's'>>;
): ArraySchema<TIn, TContext, TDefault, UnsetFlag<TFlags, 's'>>;
strip(
enabled?: true,
): ArraySchema<T, TContext, TDefault, SetFlag<TFlags, 's'>>;
): ArraySchema<TIn, TContext, TDefault, SetFlag<TFlags, 's'>>;
}
5 changes: 4 additions & 1 deletion test/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ date: {

const blDefined = bool().default(false);

// $ExpectType boolean
// $ExpectType false
blDefined.getDefault();

// $ExpectType false | undefined
Expand Down Expand Up @@ -502,6 +502,9 @@ Array: {
// $ExpectType (string | undefined)[] | undefined
array(string()).cast(null);

// $ExpectType (string | undefined)[] | null
array().defined().nullable().of(string()).cast(null);

// $ExpectType string[] | undefined
array(string().required()).validateSync(null);

Expand Down

0 comments on commit a82353f

Please sign in to comment.