Skip to content

Commit 991750c

Browse files
committed
nonNullable to disallow null
1 parent dd84925 commit 991750c

File tree

11 files changed

+2023
-20
lines changed

11 files changed

+2023
-20
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,31 @@ const nullableString = stringSchema.nullable();
11791179
nullableString.unwrap() === stringSchema; // true
11801180
```
11811181

1182+
## NonNullables
1183+
1184+
You can also make something NonNullable, using `z.nonNullable()`.
1185+
1186+
```ts
1187+
const nullableString = z.nonNullable(z.any());
1188+
nullableString.parse("asdf"); // => "asdf"
1189+
nullableString.parse(null); // fails
1190+
```
1191+
1192+
Or use the `.nonNullable()` method.
1193+
1194+
```ts
1195+
const E = z.any().nonNullable();
1196+
type E = z.infer<typeof E>; // NonNullable<any>
1197+
```
1198+
1199+
Extract the inner schema with `.unwrap()`.
1200+
1201+
```ts
1202+
const stringSchema = z.any();
1203+
const nullableString = stringSchema.nonNullable();
1204+
nullableString.unwrap() === stringSchema; // true
1205+
```
1206+
11821207
## Objects
11831208

11841209
```ts

deno/lib/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,31 @@ const nullableString = stringSchema.nullable();
11791179
nullableString.unwrap() === stringSchema; // true
11801180
```
11811181

1182+
## NonNullables
1183+
1184+
You can also make something NonNullable, using `z.nonNullable()`.
1185+
1186+
```ts
1187+
const nullableString = z.nonNullable(z.any());
1188+
nullableString.parse("asdf"); // => "asdf"
1189+
nullableString.parse(null); // fails
1190+
```
1191+
1192+
Or use the `.nonNullable()` method.
1193+
1194+
```ts
1195+
const E = z.any().nonNullable();
1196+
type E = z.infer<typeof E>; // NonNullable<any>
1197+
```
1198+
1199+
Extract the inner schema with `.unwrap()`.
1200+
1201+
```ts
1202+
const stringSchema = z.any();
1203+
const nullableString = stringSchema.nonNullable();
1204+
nullableString.unwrap() === stringSchema; // true
1205+
```
1206+
11821207
## Objects
11831208

11841209
```ts

deno/lib/__tests__/firstparty.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ test("first party switch", () => {
6868
break;
6969
case z.ZodFirstPartyTypeKind.ZodNullable:
7070
break;
71+
case z.ZodFirstPartyTypeKind.ZodNonNullable:
72+
break;
7173
case z.ZodFirstPartyTypeKind.ZodDefault:
7274
break;
7375
case z.ZodFirstPartyTypeKind.ZodCatch:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @ts-ignore TS6133
2+
import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
3+
const test = Deno.test;
4+
5+
import * as z from "../index.ts";
6+
7+
test("parses a number correctly using z....nonNullable()", () => {
8+
const a = z.nullable(z.number()).nonNullable();
9+
type a = z.infer<typeof a>;
10+
const foo: a = a.parse(5);
11+
expect(foo).toEqual(5);
12+
});
13+
14+
test("parses a number correctly using z.nonNullable(...)", () => {
15+
const a = z.nonNullable(z.any());
16+
type a = z.infer<typeof a>;
17+
const foo: a = a.parse(5);
18+
expect(foo).toEqual(5);
19+
});
20+
21+
test("fails is passed null", () => {
22+
const a = z.nullable(z.number()).nonNullable();
23+
24+
expect(() => {
25+
a.parse(null);
26+
}).toThrow(z.ZodError)
27+
});
28+
29+
test("unwrap", () => {
30+
const unwrapped = z.any().nonNullable().unwrap();
31+
expect(unwrapped).toBeInstanceOf(z.ZodAny);
32+
});

deno/lib/helpers/util.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export const ZodParsedType = util.arrayToEnum([
159159
"never",
160160
"map",
161161
"set",
162+
"nonNullable"
162163
]);
163164

164165
export type ZodParsedType = keyof typeof ZodParsedType;

deno/lib/types.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ export abstract class ZodType<
410410
this.isOptional = this.isOptional.bind(this);
411411
}
412412

413+
nonNullable(): ZodNonNullable<this> {
414+
return ZodNonNullable.create(this, this._def) as any;
415+
}
413416
optional(): ZodOptional<this> {
414417
return ZodOptional.create(this, this._def) as any;
415418
}
@@ -4455,6 +4458,56 @@ export class ZodOptional<T extends ZodTypeAny> extends ZodType<
44554458
};
44564459
}
44574460

4461+
//////////////////////////////////////////
4462+
//////////////////////////////////////////
4463+
////////// ///////////
4464+
////////// ZodNonNullable ///////////
4465+
////////// ///////////
4466+
//////////////////////////////////////////
4467+
//////////////////////////////////////////
4468+
export interface ZodNonNullableDef<T extends ZodTypeAny = ZodTypeAny>
4469+
extends ZodTypeDef {
4470+
innerType: T;
4471+
typeName: ZodFirstPartyTypeKind.ZodNonNullable;
4472+
}
4473+
4474+
export type ZodNonNullableType<T extends ZodTypeAny> = ZodNonNullable<T>;
4475+
4476+
export class ZodNonNullable<T extends ZodTypeAny> extends ZodType<
4477+
NonNullable<T["_output"]>,
4478+
ZodNonNullableDef<T>,
4479+
T["_input"]
4480+
> {
4481+
_parse(input: ParseInput): ParseReturnType<this["_output"]> {
4482+
const parsedType = this._getType(input);
4483+
if (parsedType === ZodParsedType.null) {
4484+
const { ctx } = this._processInputParams(input);
4485+
addIssueToContext(ctx, {
4486+
code: ZodIssueCode.invalid_type,
4487+
expected: ZodParsedType.nonNullable,
4488+
received: ctx.parsedType,
4489+
});
4490+
return INVALID;
4491+
}
4492+
return this._def.innerType._parse(input);
4493+
}
4494+
4495+
unwrap() {
4496+
return this._def.innerType;
4497+
}
4498+
4499+
static create = <T extends ZodTypeAny>(
4500+
type: T,
4501+
params?: RawCreateParams
4502+
): ZodNonNullable<T> => {
4503+
return new ZodNonNullable({
4504+
innerType: type,
4505+
typeName: ZodFirstPartyTypeKind.ZodNonNullable,
4506+
...processCreateParams(params),
4507+
}) as any;
4508+
};
4509+
}
4510+
44584511
///////////////////////////////////////////
44594512
///////////////////////////////////////////
44604513
////////// //////////
@@ -4927,6 +4980,7 @@ export enum ZodFirstPartyTypeKind {
49274980
ZodEffects = "ZodEffects",
49284981
ZodNativeEnum = "ZodNativeEnum",
49294982
ZodOptional = "ZodOptional",
4983+
ZodNonNullable = "ZodNonNullable",
49304984
ZodNullable = "ZodNullable",
49314985
ZodDefault = "ZodDefault",
49324986
ZodCatch = "ZodCatch",
@@ -4965,6 +5019,7 @@ export type ZodFirstPartySchemaTypes =
49655019
| ZodNativeEnum<any>
49665020
| ZodOptional<any>
49675021
| ZodNullable<any>
5022+
| ZodNonNullable<any>
49685023
| ZodDefault<any>
49695024
| ZodCatch<any>
49705025
| ZodPromise<any>
@@ -5015,6 +5070,7 @@ const promiseType = ZodPromise.create;
50155070
const effectsType = ZodEffects.create;
50165071
const optionalType = ZodOptional.create;
50175072
const nullableType = ZodNullable.create;
5073+
const nonNullableType = ZodNonNullable.create;
50185074
const preprocessType = ZodEffects.createWithPreprocess;
50195075
const pipelineType = ZodPipeline.create;
50205076
const ostring = () => stringType().optional();
@@ -5055,6 +5111,7 @@ export {
50555111
nanType as nan,
50565112
nativeEnumType as nativeEnum,
50575113
neverType as never,
5114+
nonNullableType as nonNullable,
50585115
nullType as null,
50595116
nullableType as nullable,
50605117
numberType as number,

src/__tests__/firstparty.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ test("first party switch", () => {
6767
break;
6868
case z.ZodFirstPartyTypeKind.ZodNullable:
6969
break;
70+
case z.ZodFirstPartyTypeKind.ZodNonNullable:
71+
break;
7072
case z.ZodFirstPartyTypeKind.ZodDefault:
7173
break;
7274
case z.ZodFirstPartyTypeKind.ZodCatch:

src/__tests__/nonNullable.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// @ts-ignore TS6133
2+
import { expect, test } from "@jest/globals";
3+
4+
import * as z from "../index";
5+
6+
test("parses a number correctly using z....nonNullable()", () => {
7+
const a = z.nullable(z.number()).nonNullable();
8+
type a = z.infer<typeof a>;
9+
const foo: a = a.parse(5);
10+
expect(foo).toEqual(5);
11+
});
12+
13+
test("parses a number correctly using z.nonNullable(...)", () => {
14+
const a = z.nonNullable(z.any());
15+
type a = z.infer<typeof a>;
16+
const foo: a = a.parse(5);
17+
expect(foo).toEqual(5);
18+
});
19+
20+
test("fails is passed null", () => {
21+
const a = z.nullable(z.number()).nonNullable();
22+
23+
expect(() => {
24+
a.parse(null);
25+
}).toThrow(z.ZodError)
26+
});
27+
28+
test("unwrap", () => {
29+
const unwrapped = z.any().nonNullable().unwrap();
30+
expect(unwrapped).toBeInstanceOf(z.ZodAny);
31+
});

src/helpers/util.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export const ZodParsedType = util.arrayToEnum([
159159
"never",
160160
"map",
161161
"set",
162+
"nonNullable"
162163
]);
163164

164165
export type ZodParsedType = keyof typeof ZodParsedType;

src/types.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ export abstract class ZodType<
410410
this.isOptional = this.isOptional.bind(this);
411411
}
412412

413+
nonNullable(): ZodNonNullable<this> {
414+
return ZodNonNullable.create(this, this._def) as any;
415+
}
413416
optional(): ZodOptional<this> {
414417
return ZodOptional.create(this, this._def) as any;
415418
}
@@ -4455,6 +4458,56 @@ export class ZodOptional<T extends ZodTypeAny> extends ZodType<
44554458
};
44564459
}
44574460

4461+
//////////////////////////////////////////
4462+
//////////////////////////////////////////
4463+
////////// ///////////
4464+
////////// ZodNonNullable ///////////
4465+
////////// ///////////
4466+
//////////////////////////////////////////
4467+
//////////////////////////////////////////
4468+
export interface ZodNonNullableDef<T extends ZodTypeAny = ZodTypeAny>
4469+
extends ZodTypeDef {
4470+
innerType: T;
4471+
typeName: ZodFirstPartyTypeKind.ZodNonNullable;
4472+
}
4473+
4474+
export type ZodNonNullableType<T extends ZodTypeAny> = ZodNonNullable<T>;
4475+
4476+
export class ZodNonNullable<T extends ZodTypeAny> extends ZodType<
4477+
NonNullable<T["_output"]>,
4478+
ZodNonNullableDef<T>,
4479+
T["_input"]
4480+
> {
4481+
_parse(input: ParseInput): ParseReturnType<this["_output"]> {
4482+
const parsedType = this._getType(input);
4483+
if (parsedType === ZodParsedType.null) {
4484+
const { ctx } = this._processInputParams(input);
4485+
addIssueToContext(ctx, {
4486+
code: ZodIssueCode.invalid_type,
4487+
expected: ZodParsedType.nonNullable,
4488+
received: ctx.parsedType,
4489+
});
4490+
return INVALID;
4491+
}
4492+
return this._def.innerType._parse(input);
4493+
}
4494+
4495+
unwrap() {
4496+
return this._def.innerType;
4497+
}
4498+
4499+
static create = <T extends ZodTypeAny>(
4500+
type: T,
4501+
params?: RawCreateParams
4502+
): ZodNonNullable<T> => {
4503+
return new ZodNonNullable({
4504+
innerType: type,
4505+
typeName: ZodFirstPartyTypeKind.ZodNonNullable,
4506+
...processCreateParams(params),
4507+
}) as any;
4508+
};
4509+
}
4510+
44584511
///////////////////////////////////////////
44594512
///////////////////////////////////////////
44604513
////////// //////////
@@ -4927,6 +4980,7 @@ export enum ZodFirstPartyTypeKind {
49274980
ZodEffects = "ZodEffects",
49284981
ZodNativeEnum = "ZodNativeEnum",
49294982
ZodOptional = "ZodOptional",
4983+
ZodNonNullable = "ZodNonNullable",
49304984
ZodNullable = "ZodNullable",
49314985
ZodDefault = "ZodDefault",
49324986
ZodCatch = "ZodCatch",
@@ -4965,6 +5019,7 @@ export type ZodFirstPartySchemaTypes =
49655019
| ZodNativeEnum<any>
49665020
| ZodOptional<any>
49675021
| ZodNullable<any>
5022+
| ZodNonNullable<any>
49685023
| ZodDefault<any>
49695024
| ZodCatch<any>
49705025
| ZodPromise<any>
@@ -5015,6 +5070,7 @@ const promiseType = ZodPromise.create;
50155070
const effectsType = ZodEffects.create;
50165071
const optionalType = ZodOptional.create;
50175072
const nullableType = ZodNullable.create;
5073+
const nonNullableType = ZodNonNullable.create;
50185074
const preprocessType = ZodEffects.createWithPreprocess;
50195075
const pipelineType = ZodPipeline.create;
50205076
const ostring = () => stringType().optional();
@@ -5055,6 +5111,7 @@ export {
50555111
nanType as nan,
50565112
nativeEnumType as nativeEnum,
50575113
neverType as never,
5114+
nonNullableType as nonNullable,
50585115
nullType as null,
50595116
nullableType as nullable,
50605117
numberType as number,

0 commit comments

Comments
 (0)