diff --git a/README.md b/README.md index 07878ab32..1d24dec16 100644 --- a/README.md +++ b/README.md @@ -451,6 +451,7 @@ z.number(); z.bigint(); z.boolean(); z.date(); +z.symbol(); // empty types z.undefined(); @@ -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" ``` diff --git a/deno/lib/README.md b/deno/lib/README.md index 617dec821..72c7e9bcb 100644 --- a/deno/lib/README.md +++ b/deno/lib/README.md @@ -450,6 +450,7 @@ z.number(); z.bigint(); z.boolean(); z.date(); +z.symbol(); // empty types z.undefined(); @@ -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" ``` diff --git a/deno/lib/__tests__/Mocker.ts b/deno/lib/__tests__/Mocker.ts index 4aef97a14..5aaf84bcd 100644 --- a/deno/lib/__tests__/Mocker.ts +++ b/deno/lib/__tests__/Mocker.ts @@ -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)]; @@ -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; } diff --git a/deno/lib/__tests__/primitive.test.ts b/deno/lib/__tests__/primitive.test.ts index 351eba8e7..d3f7d0f92 100644 --- a/deno/lib/__tests__/primitive.test.ts +++ b/deno/lib/__tests__/primitive.test.ts @@ -2,6 +2,7 @@ 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"; @@ -9,11 +10,15 @@ 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(); @@ -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(); @@ -130,6 +137,12 @@ test("literal bigint object", () => { expect(f).toThrow(); }); +test("literal symbol", () => { + util.assertEqual, typeof MySymbol>(true); + literalSymbolSchema.parse(MySymbol); + expect(() => literalSymbolSchema.parse(Symbol("asdf"))).toThrow(); +}); + test("parse stringSchema string", () => { stringSchema.parse(val.string); }); @@ -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(); @@ -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 = (() => T extends X ? 1 : 2) extends < - T ->() => T extends Y ? 1 : 2 - ? true - : never; - test("primitive inference", () => { - const literalStringSchemaTest: AssertEqual< - z.TypeOf, - "asdf" - > = true; - const literalNumberSchemaTest: AssertEqual< - z.TypeOf, - 12 - > = true; - const literalBooleanSchemaTest: AssertEqual< - z.TypeOf, + util.assertEqual, "asdf">(true); + util.assertEqual, 12>(true); + util.assertEqual, true>(true); + util.assertEqual, bigint>(true); + util.assertEqual, string>(true); + util.assertEqual, number>(true); + util.assertEqual, bigint>(true); + util.assertEqual, boolean>(true); + util.assertEqual, Date>(true); + util.assertEqual, symbol>(true); + + util.assertEqual, null>(true); + util.assertEqual, undefined>(true); + util.assertEqual, string | undefined>( + true + ); + util.assertEqual, string | null>(true); + util.assertEqual, number | undefined>( + true + ); + util.assertEqual, number | null>(true); + util.assertEqual, bigint | undefined>( + true + ); + util.assertEqual, bigint | null>(true); + util.assertEqual, boolean | undefined>( + true + ); + util.assertEqual, boolean | null>( + true + ); + util.assertEqual, Date | undefined>(true); + util.assertEqual, Date | null>(true); + util.assertEqual, symbol | undefined>( true - > = true; - const literalBigIntSchemaTest: AssertEqual< - z.TypeOf, - bigint - > = true; - const stringSchemaTest: AssertEqual< - z.TypeOf, - string - > = true; - const numberSchemaTest: AssertEqual< - z.TypeOf, - number - > = true; - const bigintSchemaTest: AssertEqual< - z.TypeOf, - bigint - > = true; - const booleanSchemaTest: AssertEqual< - z.TypeOf, - boolean - > = true; - const dateSchemaTest: AssertEqual, Date> = true; - const nullSchemaTest: AssertEqual, null> = true; - const undefinedSchemaTest: AssertEqual< - z.TypeOf, - undefined - > = true; - const stringSchemaOptionalTest: AssertEqual< - z.TypeOf, - string | undefined - > = true; - const stringSchemaNullableTest: AssertEqual< - z.TypeOf, - string | null - > = true; - const numberSchemaOptionalTest: AssertEqual< - z.TypeOf, - number | undefined - > = true; - const numberSchemaNullableTest: AssertEqual< - z.TypeOf, - number | null - > = true; - const bigintSchemaOptionalTest: AssertEqual< - z.TypeOf, - bigint | undefined - > = true; - const bigintSchemaNullableTest: AssertEqual< - z.TypeOf, - bigint | null - > = true; - const booleanSchemaOptionalTest: AssertEqual< - z.TypeOf, - boolean | undefined - > = true; - const booleanSchemaNullableTest: AssertEqual< - z.TypeOf, - boolean | null - > = true; - const dateSchemaOptionalTest: AssertEqual< - z.TypeOf, - Date | undefined - > = true; - const dateSchemaNullableTest: AssertEqual< - z.TypeOf, - 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, 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", () => { diff --git a/deno/lib/helpers/typeAliases.ts b/deno/lib/helpers/typeAliases.ts index 59b894d4d..3afd2bad0 100644 --- a/deno/lib/helpers/typeAliases.ts +++ b/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[]; diff --git a/deno/lib/helpers/util.ts b/deno/lib/helpers/util.ts index bcc2d4251..5660b72ef 100644 --- a/deno/lib/helpers/util.ts +++ b/deno/lib/helpers/util.ts @@ -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; diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 4dde8b910..28858ad00 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -1294,6 +1294,45 @@ export class ZodDate extends ZodType { }; } +//////////////////////////////////////////// +//////////////////////////////////////////// +////////// ////////// +////////// ZodSymbol ////////// +////////// ////////// +//////////////////////////////////////////// +//////////////////////////////////////////// +export interface ZodSymbolDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodSymbol; + symbol?: S; +} + +export class ZodSymbol extends ZodType< + S, + ZodSymbolDef +> { + _parse(input: ParseInput): ParseReturnType { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType, + }); + return INVALID; + } + + return OK(input.data); + } + + static create = (params?: RawCreateParams): ZodSymbol => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params), + }); + }; +} + //////////////////////////////////////////// //////////////////////////////////////////// ////////// ////////// @@ -4142,6 +4181,7 @@ export enum ZodFirstPartyTypeKind { ZodBigInt = "ZodBigInt", ZodBoolean = "ZodBoolean", ZodDate = "ZodDate", + ZodSymbol = "ZodSymbol", ZodUndefined = "ZodUndefined", ZodNull = "ZodNull", ZodAny = "ZodAny", @@ -4226,6 +4266,7 @@ const nanType = ZodNaN.create; const bigIntType = ZodBigInt.create; const booleanType = ZodBoolean.create; const dateType = ZodDate.create; +const symbolType = ZodSymbol.create; const undefinedType = ZodUndefined.create; const nullType = ZodNull.create; const anyType = ZodAny.create; @@ -4290,6 +4331,7 @@ export { setType as set, strictObjectType as strictObject, stringType as string, + symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, diff --git a/src/__tests__/Mocker.ts b/src/__tests__/Mocker.ts index 4aef97a14..5aaf84bcd 100644 --- a/src/__tests__/Mocker.ts +++ b/src/__tests__/Mocker.ts @@ -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)]; @@ -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; } diff --git a/src/__tests__/primitive.test.ts b/src/__tests__/primitive.test.ts index acd950fa5..28c11e713 100644 --- a/src/__tests__/primitive.test.ts +++ b/src/__tests__/primitive.test.ts @@ -1,6 +1,7 @@ // @ts-ignore TS6133 import { expect, test } from "@jest/globals"; +import { util } from "../helpers/util"; import * as z from "../index"; import { Mocker } from "./Mocker"; @@ -8,11 +9,15 @@ 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(); @@ -25,6 +30,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(); @@ -129,6 +136,12 @@ test("literal bigint object", () => { expect(f).toThrow(); }); +test("literal symbol", () => { + util.assertEqual, typeof MySymbol>(true); + literalSymbolSchema.parse(MySymbol); + expect(() => literalSymbolSchema.parse(Symbol("asdf"))).toThrow(); +}); + test("parse stringSchema string", () => { stringSchema.parse(val.string); }); @@ -277,6 +290,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(); @@ -325,121 +374,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 = (() => T extends X ? 1 : 2) extends < - T ->() => T extends Y ? 1 : 2 - ? true - : never; - test("primitive inference", () => { - const literalStringSchemaTest: AssertEqual< - z.TypeOf, - "asdf" - > = true; - const literalNumberSchemaTest: AssertEqual< - z.TypeOf, - 12 - > = true; - const literalBooleanSchemaTest: AssertEqual< - z.TypeOf, + util.assertEqual, "asdf">(true); + util.assertEqual, 12>(true); + util.assertEqual, true>(true); + util.assertEqual, bigint>(true); + util.assertEqual, string>(true); + util.assertEqual, number>(true); + util.assertEqual, bigint>(true); + util.assertEqual, boolean>(true); + util.assertEqual, Date>(true); + util.assertEqual, symbol>(true); + + util.assertEqual, null>(true); + util.assertEqual, undefined>(true); + util.assertEqual, string | undefined>( + true + ); + util.assertEqual, string | null>(true); + util.assertEqual, number | undefined>( + true + ); + util.assertEqual, number | null>(true); + util.assertEqual, bigint | undefined>( + true + ); + util.assertEqual, bigint | null>(true); + util.assertEqual, boolean | undefined>( + true + ); + util.assertEqual, boolean | null>( + true + ); + util.assertEqual, Date | undefined>(true); + util.assertEqual, Date | null>(true); + util.assertEqual, symbol | undefined>( true - > = true; - const literalBigIntSchemaTest: AssertEqual< - z.TypeOf, - bigint - > = true; - const stringSchemaTest: AssertEqual< - z.TypeOf, - string - > = true; - const numberSchemaTest: AssertEqual< - z.TypeOf, - number - > = true; - const bigintSchemaTest: AssertEqual< - z.TypeOf, - bigint - > = true; - const booleanSchemaTest: AssertEqual< - z.TypeOf, - boolean - > = true; - const dateSchemaTest: AssertEqual, Date> = true; - const nullSchemaTest: AssertEqual, null> = true; - const undefinedSchemaTest: AssertEqual< - z.TypeOf, - undefined - > = true; - const stringSchemaOptionalTest: AssertEqual< - z.TypeOf, - string | undefined - > = true; - const stringSchemaNullableTest: AssertEqual< - z.TypeOf, - string | null - > = true; - const numberSchemaOptionalTest: AssertEqual< - z.TypeOf, - number | undefined - > = true; - const numberSchemaNullableTest: AssertEqual< - z.TypeOf, - number | null - > = true; - const bigintSchemaOptionalTest: AssertEqual< - z.TypeOf, - bigint | undefined - > = true; - const bigintSchemaNullableTest: AssertEqual< - z.TypeOf, - bigint | null - > = true; - const booleanSchemaOptionalTest: AssertEqual< - z.TypeOf, - boolean | undefined - > = true; - const booleanSchemaNullableTest: AssertEqual< - z.TypeOf, - boolean | null - > = true; - const dateSchemaOptionalTest: AssertEqual< - z.TypeOf, - Date | undefined - > = true; - const dateSchemaNullableTest: AssertEqual< - z.TypeOf, - 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, 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", () => { diff --git a/src/helpers/typeAliases.ts b/src/helpers/typeAliases.ts index 59b894d4d..3afd2bad0 100644 --- a/src/helpers/typeAliases.ts +++ b/src/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[]; diff --git a/src/helpers/util.ts b/src/helpers/util.ts index bcc2d4251..5660b72ef 100644 --- a/src/helpers/util.ts +++ b/src/helpers/util.ts @@ -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; diff --git a/src/types.ts b/src/types.ts index 75a835806..063a617c3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1294,6 +1294,45 @@ export class ZodDate extends ZodType { }; } +//////////////////////////////////////////// +//////////////////////////////////////////// +////////// ////////// +////////// ZodSymbol ////////// +////////// ////////// +//////////////////////////////////////////// +//////////////////////////////////////////// +export interface ZodSymbolDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodSymbol; + symbol?: S; +} + +export class ZodSymbol extends ZodType< + S, + ZodSymbolDef +> { + _parse(input: ParseInput): ParseReturnType { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType, + }); + return INVALID; + } + + return OK(input.data); + } + + static create = (params?: RawCreateParams): ZodSymbol => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params), + }); + }; +} + //////////////////////////////////////////// //////////////////////////////////////////// ////////// ////////// @@ -4142,6 +4181,7 @@ export enum ZodFirstPartyTypeKind { ZodBigInt = "ZodBigInt", ZodBoolean = "ZodBoolean", ZodDate = "ZodDate", + ZodSymbol = "ZodSymbol", ZodUndefined = "ZodUndefined", ZodNull = "ZodNull", ZodAny = "ZodAny", @@ -4226,6 +4266,7 @@ const nanType = ZodNaN.create; const bigIntType = ZodBigInt.create; const booleanType = ZodBoolean.create; const dateType = ZodDate.create; +const symbolType = ZodSymbol.create; const undefinedType = ZodUndefined.create; const nullType = ZodNull.create; const anyType = ZodAny.create; @@ -4290,6 +4331,7 @@ export { setType as set, strictObjectType as strictObject, stringType as string, + symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined,