Skip to content

Commit

Permalink
updated changelog with beta.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin McDonnell committed May 3, 2021
2 parents 363087e + 1c7974b commit bb6aa90
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 133 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

### 3.0.0-beta.1

- Moved default value logic into ZodDefault. Implemented `.nullish()` method.

### 3.0.0-alpha.33

- Added `.returnType` and `.parameters` methods to ZodFunction
Expand Down
2 changes: 1 addition & 1 deletion coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 12 additions & 7 deletions deno/lib/__tests__/default.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test("default with transform", () => {
.transform((val) => val.toUpperCase())
.default("default");
expect(stringWithDefault.parse(undefined)).toBe("DEFAULT");
expect(stringWithDefault).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault).toBeInstanceOf(z.ZodDefault);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodEffects);
expect(stringWithDefault._def.innerType._def.schema).toBeInstanceOf(
z.ZodSchema
Expand All @@ -32,15 +32,16 @@ test("default with transform", () => {
test("default on existing optional", () => {
const stringWithDefault = z.string().optional().default("asdf");
expect(stringWithDefault.parse(undefined)).toBe("asdf");
expect(stringWithDefault).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault).toBeInstanceOf(z.ZodDefault);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault._def.innerType._def.innerType).toBeInstanceOf(
z.ZodString
);

type inp = z.input<typeof stringWithDefault>;
const f1: util.AssertEqual<inp, string | undefined> = true;
type out = z.output<typeof stringWithDefault>;
const f2: util.AssertEqual<out, string | undefined> = true;
const f2: util.AssertEqual<out, string> = true;
f1;
f2;
});
Expand All @@ -51,7 +52,7 @@ test("optional on default", () => {
type inp = z.input<typeof stringWithDefault>;
const f1: util.AssertEqual<inp, string | undefined> = true;
type out = z.output<typeof stringWithDefault>;
const f2: util.AssertEqual<out, string> = true;
const f2: util.AssertEqual<out, string | undefined> = true;
f1;
f2;
});
Expand All @@ -60,7 +61,6 @@ test("complex chain example", () => {
const complex = z
.string()
.default("asdf")
.optional()
.transform((val) => val.toUpperCase())
.default("qwer")
.removeDefault()
Expand All @@ -74,9 +74,8 @@ test("removeDefault", () => {
const stringWithRemovedDefault = z.string().default("asdf").removeDefault();

type out = z.output<typeof stringWithRemovedDefault>;
const f2: util.AssertEqual<out, string | undefined> = true;
const f2: util.AssertEqual<out, string> = true;
f2;
expect(stringWithRemovedDefault.parse(undefined)).toBe(undefined);
});

test("nested", () => {
Expand All @@ -97,3 +96,9 @@ test("nested", () => {
expect(outer.parse({})).toEqual({ inner: "asdf" });
expect(outer.parse({ inner: undefined })).toEqual({ inner: "asdf" });
});

test("chained defaults", () => {
const stringWithDefault = z.string().default("inner").default("outer");
const result = stringWithDefault.parse(undefined);
expect(result).toEqual("outer");
});
4 changes: 2 additions & 2 deletions deno/lib/__tests__/partials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ test("required", () => {
const object = z.object({
name: z.string(),
age: z.number().optional(),
field: z.string().optional().default(undefined),
field: z.string().optional().default("asdf"),
});

const requiredObject = object.required();
expect(requiredObject.shape.name).toBeInstanceOf(z.ZodString);
expect(requiredObject.shape.age).toBeInstanceOf(z.ZodNumber);
expect(requiredObject.shape.field).toBeInstanceOf(z.ZodString);
expect(requiredObject.shape.field).toBeInstanceOf(z.ZodDefault);
});
124 changes: 67 additions & 57 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,13 @@ export abstract class ZodType<
this.default = this.default.bind(this);
}

optional: <This extends this = this>() => ZodOptionalType<This> = () =>
optional: <This extends this = this>() => ZodOptional<This> = () =>
ZodOptional.create(this) as any;
nullable: <This extends this = this>() => ZodNullableType<This> = () =>
nullable: <This extends this = this>() => ZodNullable<This> = () =>
ZodNullable.create(this) as any;
nullish: <This extends this = this>() => ZodNullable<
ZodOptional<This>
> = () => this.optional().nullable();

array: () => ZodArray<this> = () => ZodArray.create(this);

Expand Down Expand Up @@ -359,12 +362,12 @@ export abstract class ZodType<
return returnType;
}

default<T extends Input, This extends this = this>(
def: T
): ZodOptional<This, true>;
default<T extends () => Input, This extends this = this>(
def: T
): ZodOptional<This, true>;
default<This extends this = this>(
def: util.noUndefined<Input>
): ZodDefault<This>;
default<This extends this = this>(
def: () => util.noUndefined<Input>
): ZodDefault<This>;
default(def: any) {
const defaultValueFunc = typeof def === "function" ? def : () => def;
// if (this instanceof ZodOptional) {
Expand All @@ -373,7 +376,7 @@ export abstract class ZodType<
// defaultValue: defaultValueFunc,
// }) as any;
// }
return new ZodOptional({
return new ZodDefault({
innerType: this,
defaultValue: defaultValueFunc,
}) as any;
Expand Down Expand Up @@ -1298,7 +1301,7 @@ export type objectInputType<
baseObjectInputType<Shape> & { [k: string]: Catchall["_input"] }
>;

type deoptional<T extends ZodTypeAny> = T extends ZodOptional<infer U, any>
type deoptional<T extends ZodTypeAny> = T extends ZodOptional<infer U>
? deoptional<U>
: T;

Expand Down Expand Up @@ -1578,7 +1581,7 @@ export class ZodObject<
const fieldSchema = this.shape[key];
let newField = fieldSchema;
while (newField instanceof ZodOptional) {
newField = (newField as ZodOptional<any, any>)._def.innerType;
newField = (newField as ZodOptional<any>)._def.innerType;
}

newShape[key] = newField;
Expand Down Expand Up @@ -2597,40 +2600,19 @@ export { ZodEffects as ZodTransformer };
export interface ZodOptionalDef<T extends ZodTypeAny = ZodTypeAny>
extends ZodTypeDef {
innerType: T;
defaultValue: undefined | (() => T["_input"]);
}

export type addDefaultToOptional<
T extends ZodOptional<any, any>
> = T extends ZodOptional<infer U, any> ? ZodOptional<U, true> : never;

export type removeDefaultFromOptional<
T extends ZodOptional<any, any>
> = T extends ZodOptional<infer U, any> ? ZodOptional<U, false> : never;

export type ZodOptionalType<T extends ZodTypeAny> = T extends ZodOptional<
infer U,
infer H
>
? ZodOptional<U, H>
: ZodOptional<T, false>; // no default by default
export type ZodOptionalType<T extends ZodTypeAny> = ZodOptional<T>;

export class ZodOptional<
T extends ZodTypeAny,
HasDefault extends boolean = false
> extends ZodType<
HasDefault extends true ? T["_output"] : T["_output"] | undefined,
export class ZodOptional<T extends ZodTypeAny> extends ZodType<
T["_output"] | undefined,
ZodOptionalDef<T>,
T["_input"] | undefined
> {
_parse(ctx: ParseContext): any {
let data = ctx.data;
const data = ctx.data;
if (ctx.parsedType === ZodParsedType.undefined) {
if (this._def.defaultValue !== undefined) {
data = this._def.defaultValue();
} else {
return undefined;
}
return undefined;
}

return new PseudoPromise().then(() => {
Expand All @@ -2645,18 +2627,9 @@ export class ZodOptional<
return this._def.innerType;
}

removeDefault(): ZodOptional<T, false> {
return new ZodOptional({
...this._def,
defaultValue: undefined,
});
}

static create = <T extends ZodTypeAny>(type: T): ZodOptionalType<T> => {
if (type instanceof ZodOptional) return type as any;
static create = <T extends ZodTypeAny>(type: T): ZodOptional<T> => {
return new ZodOptional({
innerType: type,
defaultValue: undefined,
}) as any;
};
}
Expand All @@ -2673,12 +2646,7 @@ export interface ZodNullableDef<T extends ZodTypeAny = ZodTypeAny>
innerType: T;
}

// This type allows for nullable flattening
export type ZodNullableType<T extends ZodTypeAny> = T extends ZodNullable<
infer U
>
? ZodNullable<U>
: ZodNullable<T>;
export type ZodNullableType<T extends ZodTypeAny> = ZodNullable<T>;

export class ZodNullable<T extends ZodTypeAny> extends ZodType<
T["_output"] | null,
Expand All @@ -2702,15 +2670,56 @@ export class ZodNullable<T extends ZodTypeAny> extends ZodType<
return this._def.innerType;
}

static create = <T extends ZodTypeAny>(type: T): ZodNullableType<T> => {
// An nullable nullable is the original nullable
if (type instanceof ZodNullable) return type as any;
static create = <T extends ZodTypeAny>(type: T): ZodNullable<T> => {
return new ZodNullable({
innerType: type,
}) as any;
};
}

////////////////////////////////////////////
////////////////////////////////////////////
////////// //////////
////////// ZodDefault //////////
////////// //////////
////////////////////////////////////////////
////////////////////////////////////////////
export interface ZodDefaultDef<T extends ZodTypeAny = ZodTypeAny>
extends ZodTypeDef {
innerType: T;
defaultValue: () => util.noUndefined<T["_input"]>;
}

export class ZodDefault<T extends ZodTypeAny> extends ZodType<
util.noUndefined<T["_output"]>,
ZodDefaultDef<T>,
T["_input"] | undefined
> {
_parse(ctx: ParseContext): any {
let data = ctx.data;
if (ctx.parsedType === ZodParsedType.undefined) {
data = this._def.defaultValue();
}

return new PseudoPromise().then(() => {
return this._def.innerType._parseWithInvalidFallback(data, {
...ctx,
parentError: ctx.currentError,
});
});
}

removeDefault() {
return this._def.innerType;
}

static create = <T extends ZodTypeAny>(type: T): ZodOptional<T> => {
return new ZodOptional({
innerType: type,
}) as any;
};
}

export const custom = <T>(
check?: (data: unknown) => any,
params?: Parameters<ZodTypeAny["refine"]>[1]
Expand Down Expand Up @@ -2751,8 +2760,9 @@ export type ZodFirstPartySchemaTypes =
| ZodEnum<any>
| ZodEffects<any>
| ZodNativeEnum<any>
| ZodOptional<any, any>
| ZodOptional<any>
| ZodNullable<any>
| ZodDefault<any>
| ZodPromise<any>;

const instanceOfType = <T extends new (...args: any[]) => any>(
Expand Down
19 changes: 12 additions & 7 deletions src/__tests__/default.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test("default with transform", () => {
.transform((val) => val.toUpperCase())
.default("default");
expect(stringWithDefault.parse(undefined)).toBe("DEFAULT");
expect(stringWithDefault).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault).toBeInstanceOf(z.ZodDefault);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodEffects);
expect(stringWithDefault._def.innerType._def.schema).toBeInstanceOf(
z.ZodSchema
Expand All @@ -31,15 +31,16 @@ test("default with transform", () => {
test("default on existing optional", () => {
const stringWithDefault = z.string().optional().default("asdf");
expect(stringWithDefault.parse(undefined)).toBe("asdf");
expect(stringWithDefault).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault).toBeInstanceOf(z.ZodDefault);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault._def.innerType._def.innerType).toBeInstanceOf(
z.ZodString
);

type inp = z.input<typeof stringWithDefault>;
const f1: util.AssertEqual<inp, string | undefined> = true;
type out = z.output<typeof stringWithDefault>;
const f2: util.AssertEqual<out, string | undefined> = true;
const f2: util.AssertEqual<out, string> = true;
f1;
f2;
});
Expand All @@ -50,7 +51,7 @@ test("optional on default", () => {
type inp = z.input<typeof stringWithDefault>;
const f1: util.AssertEqual<inp, string | undefined> = true;
type out = z.output<typeof stringWithDefault>;
const f2: util.AssertEqual<out, string> = true;
const f2: util.AssertEqual<out, string | undefined> = true;
f1;
f2;
});
Expand All @@ -59,7 +60,6 @@ test("complex chain example", () => {
const complex = z
.string()
.default("asdf")
.optional()
.transform((val) => val.toUpperCase())
.default("qwer")
.removeDefault()
Expand All @@ -73,9 +73,8 @@ test("removeDefault", () => {
const stringWithRemovedDefault = z.string().default("asdf").removeDefault();

type out = z.output<typeof stringWithRemovedDefault>;
const f2: util.AssertEqual<out, string | undefined> = true;
const f2: util.AssertEqual<out, string> = true;
f2;
expect(stringWithRemovedDefault.parse(undefined)).toBe(undefined);
});

test("nested", () => {
Expand All @@ -96,3 +95,9 @@ test("nested", () => {
expect(outer.parse({})).toEqual({ inner: "asdf" });
expect(outer.parse({ inner: undefined })).toEqual({ inner: "asdf" });
});

test("chained defaults", () => {
const stringWithDefault = z.string().default("inner").default("outer");
const result = stringWithDefault.parse(undefined);
expect(result).toEqual("outer");
});
4 changes: 2 additions & 2 deletions src/__tests__/partials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ test("required", () => {
const object = z.object({
name: z.string(),
age: z.number().optional(),
field: z.string().optional().default(undefined),
field: z.string().optional().default("asdf"),
});

const requiredObject = object.required();
expect(requiredObject.shape.name).toBeInstanceOf(z.ZodString);
expect(requiredObject.shape.age).toBeInstanceOf(z.ZodNumber);
expect(requiredObject.shape.field).toBeInstanceOf(z.ZodString);
expect(requiredObject.shape.field).toBeInstanceOf(z.ZodDefault);
});
Loading

0 comments on commit bb6aa90

Please sign in to comment.