Skip to content

Commit

Permalink
Add ZodSymbol (#1448)
Browse files Browse the repository at this point in the history
* Wire up ZodParsedType.symbol

* Add ZodSymbol

* Add specs for symbol schema

* Add support for specific symbols

* Add compile time validation on ZodSymbol.create

* Rename variable and fix spec

* Move literal symbol logic into z.literal, add docs

* Fix ZodSymbol.create

* Fix ZodSymbol.create

Co-authored-by: santosmarco <eu@marco.rio.br>
Co-authored-by: Colin McDonnell <colinmcd@alum.mit.edu>
  • Loading branch information
3 people committed Nov 15, 2022
1 parent 248e4da commit 3d8d336
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 228 deletions.
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -451,6 +451,7 @@ z.number();
z.bigint();
z.boolean();
z.date();
z.symbol();

// empty types
z.undefined();
Expand All @@ -472,8 +473,12 @@ z.never();
```ts
const tuna = z.literal("tuna");
const twelve = z.literal(12);
const twobig = z.literal(2n); // bigint literal
const tru = z.literal(true);

const terrificSymbol = Symbol("terrific");
const terrific = z.literal(terrificSymbol);

// retrieve literal value
tuna.value; // "tuna"
```
Expand Down
5 changes: 5 additions & 0 deletions deno/lib/README.md
Expand Up @@ -450,6 +450,7 @@ z.number();
z.bigint();
z.boolean();
z.date();
z.symbol();

// empty types
z.undefined();
Expand All @@ -471,8 +472,12 @@ z.never();
```ts
const tuna = z.literal("tuna");
const twelve = z.literal(12);
const twobig = z.literal(2n); // bigint literal
const tru = z.literal(true);

const terrificSymbol = Symbol("terrific");
const terrific = z.literal(terrificSymbol);

// retrieve literal value
tuna.value; // "tuna"
```
Expand Down
5 changes: 5 additions & 0 deletions deno/lib/__tests__/Mocker.ts
Expand Up @@ -2,6 +2,8 @@ function getRandomInt(max: number) {
return Math.floor(Math.random() * Math.floor(max));
}

const testSymbol = Symbol("test");

export class Mocker {
pick = (...args: any[]) => {
return args[getRandomInt(args.length)];
Expand All @@ -22,6 +24,9 @@ export class Mocker {
get date() {
return new Date(Math.floor(Date.now() * Math.random()));
}
get symbol() {
return testSymbol;
}
get null(): null {
return null;
}
Expand Down
227 changes: 114 additions & 113 deletions deno/lib/__tests__/primitive.test.ts
Expand Up @@ -2,18 +2,23 @@
import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
const test = Deno.test;

import { util } from "../helpers/util.ts";
import * as z from "../index.ts";
import { Mocker } from "./Mocker.ts";

const literalStringSchema = z.literal("asdf");
const literalNumberSchema = z.literal(12);
const literalBooleanSchema = z.literal(true);
const literalBigIntSchema = z.literal(BigInt(42));
const MySymbol = Symbol("stuff");
const literalSymbolSchema = z.literal(MySymbol);
const stringSchema = z.string();
const numberSchema = z.number();
const bigintSchema = z.bigint();
const booleanSchema = z.boolean();
const dateSchema = z.date();
const symbolSchema = z.symbol();

const nullSchema = z.null();
const undefinedSchema = z.undefined();
const stringSchemaOptional = z.string().optional();
Expand All @@ -26,6 +31,8 @@ const booleanSchemaOptional = z.boolean().optional();
const booleanSchemaNullable = z.boolean().nullable();
const dateSchemaOptional = z.date().optional();
const dateSchemaNullable = z.date().nullable();
const symbolSchemaOptional = z.symbol().optional();
const symbolSchemaNullable = z.symbol().nullable();

const val = new Mocker();

Expand Down Expand Up @@ -130,6 +137,12 @@ test("literal bigint object", () => {
expect(f).toThrow();
});

test("literal symbol", () => {
util.assertEqual<z.infer<typeof literalSymbolSchema>, typeof MySymbol>(true);
literalSymbolSchema.parse(MySymbol);
expect(() => literalSymbolSchema.parse(Symbol("asdf"))).toThrow();
});

test("parse stringSchema string", () => {
stringSchema.parse(val.string);
});
Expand Down Expand Up @@ -278,6 +291,42 @@ test("parse dateSchema invalid date", async () => {
});
// ==============

test("parse symbolSchema string", () => {
const f = () => symbolSchema.parse(val.string);
expect(f).toThrow();
});

test("parse symbolSchema number", () => {
const f = () => symbolSchema.parse(val.number);
expect(f).toThrow();
});

test("parse symbolSchema boolean", () => {
const f = () => symbolSchema.parse(val.boolean);
expect(f).toThrow();
});

test("parse symbolSchema date", () => {
const f = () => symbolSchema.parse(val.date);
expect(f).toThrow();
});

test("parse symbolSchema symbol", () => {
symbolSchema.parse(val.symbol);
});

test("parse symbolSchema undefined", () => {
const f = () => symbolSchema.parse(val.undefined);
expect(f).toThrow();
});

test("parse symbolSchema null", () => {
const f = () => symbolSchema.parse(val.null);
expect(f).toThrow();
});

// ==============

test("parse undefinedSchema string", () => {
const f = () => undefinedSchema.parse(val.string);
expect(f).toThrow();
Expand Down Expand Up @@ -326,121 +375,73 @@ test("parse nullSchema null", () => {
nullSchema.parse(val.null);
});

export type AssertEqualTest = boolean | undefined extends true
? true extends boolean | undefined
? true
: never
: never;

type AssertEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
T
>() => T extends Y ? 1 : 2
? true
: never;

test("primitive inference", () => {
const literalStringSchemaTest: AssertEqual<
z.TypeOf<typeof literalStringSchema>,
"asdf"
> = true;
const literalNumberSchemaTest: AssertEqual<
z.TypeOf<typeof literalNumberSchema>,
12
> = true;
const literalBooleanSchemaTest: AssertEqual<
z.TypeOf<typeof literalBooleanSchema>,
util.assertEqual<z.TypeOf<typeof literalStringSchema>, "asdf">(true);
util.assertEqual<z.TypeOf<typeof literalNumberSchema>, 12>(true);
util.assertEqual<z.TypeOf<typeof literalBooleanSchema>, true>(true);
util.assertEqual<z.TypeOf<typeof literalBigIntSchema>, bigint>(true);
util.assertEqual<z.TypeOf<typeof stringSchema>, string>(true);
util.assertEqual<z.TypeOf<typeof numberSchema>, number>(true);
util.assertEqual<z.TypeOf<typeof bigintSchema>, bigint>(true);
util.assertEqual<z.TypeOf<typeof booleanSchema>, boolean>(true);
util.assertEqual<z.TypeOf<typeof dateSchema>, Date>(true);
util.assertEqual<z.TypeOf<typeof symbolSchema>, symbol>(true);

util.assertEqual<z.TypeOf<typeof nullSchema>, null>(true);
util.assertEqual<z.TypeOf<typeof undefinedSchema>, undefined>(true);
util.assertEqual<z.TypeOf<typeof stringSchemaOptional>, string | undefined>(
true
);
util.assertEqual<z.TypeOf<typeof stringSchemaNullable>, string | null>(true);
util.assertEqual<z.TypeOf<typeof numberSchemaOptional>, number | undefined>(
true
);
util.assertEqual<z.TypeOf<typeof numberSchemaNullable>, number | null>(true);
util.assertEqual<z.TypeOf<typeof bigintSchemaOptional>, bigint | undefined>(
true
);
util.assertEqual<z.TypeOf<typeof bigintSchemaNullable>, bigint | null>(true);
util.assertEqual<z.TypeOf<typeof booleanSchemaOptional>, boolean | undefined>(
true
);
util.assertEqual<z.TypeOf<typeof booleanSchemaNullable>, boolean | null>(
true
);
util.assertEqual<z.TypeOf<typeof dateSchemaOptional>, Date | undefined>(true);
util.assertEqual<z.TypeOf<typeof dateSchemaNullable>, Date | null>(true);
util.assertEqual<z.TypeOf<typeof symbolSchemaOptional>, symbol | undefined>(
true
> = true;
const literalBigIntSchemaTest: AssertEqual<
z.TypeOf<typeof literalBigIntSchema>,
bigint
> = true;
const stringSchemaTest: AssertEqual<
z.TypeOf<typeof stringSchema>,
string
> = true;
const numberSchemaTest: AssertEqual<
z.TypeOf<typeof numberSchema>,
number
> = true;
const bigintSchemaTest: AssertEqual<
z.TypeOf<typeof bigintSchema>,
bigint
> = true;
const booleanSchemaTest: AssertEqual<
z.TypeOf<typeof booleanSchema>,
boolean
> = true;
const dateSchemaTest: AssertEqual<z.TypeOf<typeof dateSchema>, Date> = true;
const nullSchemaTest: AssertEqual<z.TypeOf<typeof nullSchema>, null> = true;
const undefinedSchemaTest: AssertEqual<
z.TypeOf<typeof undefinedSchema>,
undefined
> = true;
const stringSchemaOptionalTest: AssertEqual<
z.TypeOf<typeof stringSchemaOptional>,
string | undefined
> = true;
const stringSchemaNullableTest: AssertEqual<
z.TypeOf<typeof stringSchemaNullable>,
string | null
> = true;
const numberSchemaOptionalTest: AssertEqual<
z.TypeOf<typeof numberSchemaOptional>,
number | undefined
> = true;
const numberSchemaNullableTest: AssertEqual<
z.TypeOf<typeof numberSchemaNullable>,
number | null
> = true;
const bigintSchemaOptionalTest: AssertEqual<
z.TypeOf<typeof bigintSchemaOptional>,
bigint | undefined
> = true;
const bigintSchemaNullableTest: AssertEqual<
z.TypeOf<typeof bigintSchemaNullable>,
bigint | null
> = true;
const booleanSchemaOptionalTest: AssertEqual<
z.TypeOf<typeof booleanSchemaOptional>,
boolean | undefined
> = true;
const booleanSchemaNullableTest: AssertEqual<
z.TypeOf<typeof booleanSchemaNullable>,
boolean | null
> = true;
const dateSchemaOptionalTest: AssertEqual<
z.TypeOf<typeof dateSchemaOptional>,
Date | undefined
> = true;
const dateSchemaNullableTest: AssertEqual<
z.TypeOf<typeof dateSchemaNullable>,
Date | null
> = true;

[
literalStringSchemaTest,
literalNumberSchemaTest,
literalBooleanSchemaTest,
literalBigIntSchemaTest,
stringSchemaTest,
numberSchemaTest,
bigintSchemaTest,
booleanSchemaTest,
dateSchemaTest,
nullSchemaTest,
undefinedSchemaTest,
stringSchemaOptionalTest,
stringSchemaNullableTest,
numberSchemaOptionalTest,
numberSchemaNullableTest,
bigintSchemaOptionalTest,
bigintSchemaNullableTest,
booleanSchemaOptionalTest,
booleanSchemaNullableTest,
dateSchemaOptionalTest,
dateSchemaNullableTest,
];
);
util.assertEqual<z.TypeOf<typeof symbolSchemaNullable>, symbol | null>(true);

// [
// literalStringSchemaTest,
// literalNumberSchemaTest,
// literalBooleanSchemaTest,
// literalBigIntSchemaTest,
// stringSchemaTest,
// numberSchemaTest,
// bigintSchemaTest,
// booleanSchemaTest,
// dateSchemaTest,
// symbolSchemaTest,

// nullSchemaTest,
// undefinedSchemaTest,
// stringSchemaOptionalTest,
// stringSchemaNullableTest,
// numberSchemaOptionalTest,
// numberSchemaNullableTest,
// bigintSchemaOptionalTest,
// bigintSchemaNullableTest,
// booleanSchemaOptionalTest,
// booleanSchemaNullableTest,
// dateSchemaOptionalTest,
// dateSchemaNullableTest,
// symbolSchemaOptionalTest,
// symbolSchemaNullableTest,

// ];
});

test("get literal value", () => {
Expand Down
9 changes: 8 additions & 1 deletion deno/lib/helpers/typeAliases.ts
@@ -1,2 +1,9 @@
export type Primitive = string | number | bigint | boolean | null | undefined;
export type Primitive =
| string
| number
| symbol
| bigint
| boolean
| null
| undefined;
export type Scalars = Primitive | Primitive[];
3 changes: 3 additions & 0 deletions deno/lib/helpers/util.ts
Expand Up @@ -140,6 +140,9 @@ export const getParsedType = (data: any): ZodParsedType => {
case "bigint":
return ZodParsedType.bigint;

case "symbol":
return ZodParsedType.symbol;

case "object":
if (Array.isArray(data)) {
return ZodParsedType.array;
Expand Down

0 comments on commit 3d8d336

Please sign in to comment.