From 3922dddfb17e04500b49ce0c5d838fbb4d6b598b Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Tue, 12 Nov 2024 19:11:26 -0800 Subject: [PATCH 1/6] Implement Standard Schema --- deno/lib/__tests__/standard-schema.test.ts | 84 +++++++++++++++ deno/lib/standard-schema.ts | 113 +++++++++++++++++++++ deno/lib/types.ts | 58 ++++++++++- package.json | 23 ++++- src/__tests__/standard-schema.test.ts | 83 +++++++++++++++ src/standard-schema.ts | 113 +++++++++++++++++++++ src/types.ts | 58 ++++++++++- yarn.lock | 5 + 8 files changed, 531 insertions(+), 6 deletions(-) create mode 100644 deno/lib/__tests__/standard-schema.test.ts create mode 100644 deno/lib/standard-schema.ts create mode 100644 src/__tests__/standard-schema.test.ts create mode 100644 src/standard-schema.ts diff --git a/deno/lib/__tests__/standard-schema.test.ts b/deno/lib/__tests__/standard-schema.test.ts new file mode 100644 index 000000000..bb9497c9a --- /dev/null +++ b/deno/lib/__tests__/standard-schema.test.ts @@ -0,0 +1,84 @@ +// @ts-ignore TS6133 +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 type { v1 } from "@standard-schema/spec"; + +test("assignability", () => { + const _s1: v1.StandardSchema = z.string(); + const _s2: v1.StandardSchema = z.string(); + const _s3: v1.StandardSchema = z.string(); + const _s4: v1.StandardSchema = z.string(); + [_s1, _s2, _s3, _s4]; +}); + +test("type inference", () => { + const stringToNumber = z.string().transform((x) => x.length); + type input = v1.InferInput; + util.assertEqual(true); + type output = v1.InferOutput; + util.assertEqual(true); +}); + +test("valid parse", () => { + const schema = z.string(); + const result = schema["~standard"]["validate"]("hello"); + if (result instanceof Promise) { + throw new Error("Expected sync result"); + } + expect(result.issues).toEqual(undefined); + if (result.issues) { + throw new Error("Expected no issues"); + } else { + expect(result.value).toEqual("hello"); + } +}); + +test("invalid parse", () => { + const schema = z.string(); + const result = schema["~standard"]["validate"](1234); + if (result instanceof Promise) { + throw new Error("Expected sync result"); + } + expect(result.issues).toBeDefined(); + if (!result.issues) { + throw new Error("Expected issues"); + } + expect(result.issues.length).toEqual(1); + expect(result.issues[0].path).toEqual([]); +}); + +test("valid parse async", async () => { + const schema = z.string().refine(async () => true); + const _result = schema["~standard"]["validate"]("hello"); + if (_result instanceof Promise) { + const result = await _result; + expect(result.issues).toEqual(undefined); + if (result.issues) { + throw new Error("Expected no issues"); + } else { + expect(result.value).toEqual("hello"); + } + } else { + throw new Error("Expected async result"); + } +}); + +test("invalid parse async", async () => { + const schema = z.string().refine(async () => false); + const _result = schema["~standard"]["validate"]("hello"); + if (_result instanceof Promise) { + const result = await _result; + expect(result.issues).toBeDefined(); + if (!result.issues) { + throw new Error("Expected issues"); + } + expect(result.issues.length).toEqual(1); + expect(result.issues[0].path).toEqual([]); + } else { + throw new Error("Expected async result"); + } +}); diff --git a/deno/lib/standard-schema.ts b/deno/lib/standard-schema.ts new file mode 100644 index 000000000..a946c1b23 --- /dev/null +++ b/deno/lib/standard-schema.ts @@ -0,0 +1,113 @@ +declare namespace v1 { + /** + * The Standard Schema interface. + */ + interface StandardSchema { + /** + * The Standard Schema properties. + */ + readonly "~standard": StandardSchemaProps; + } + /** + * The Standard Schema properties interface. + */ + interface StandardSchemaProps { + /** + * The version number of the standard. + */ + readonly version: 1; + /** + * The vendor name of the schema library. + */ + readonly vendor: string; + /** + * Validates unknown input values. + */ + readonly validate: ( + value: unknown + ) => StandardResult | Promise>; + /** + * Inferred types associated with the schema. + */ + readonly types?: StandardTypes | undefined; + } + /** + * The result interface of the validate function. + */ + type StandardResult = + | StandardSuccessResult + | StandardFailureResult; + /** + * The result interface if validation succeeds. + */ + interface StandardSuccessResult { + /** + * The typed output value. + */ + readonly value: Output; + /** + * The non-existent issues. + */ + readonly issues?: undefined; + } + /** + * The result interface if validation fails. + */ + interface StandardFailureResult { + /** + * The issues of failed validation. + */ + readonly issues: ReadonlyArray; + } + /** + * The issue interface of the failure output. + */ + interface StandardIssue { + /** + * The error message of the issue. + */ + readonly message: string; + /** + * The path of the issue, if any. + */ + readonly path?: + | ReadonlyArray + | undefined; + } + /** + * The path segment interface of the issue. + */ + interface StandardPathSegment { + /** + * The key representing a path segment. + */ + readonly key: PropertyKey; + } + /** + * The base types interface of Standard Schema. + */ + interface StandardTypes { + /** + * The input type of the schema. + */ + readonly input: Input; + /** + * The output type of the schema. + */ + readonly output: Output; + } + /** + * Infers the input type of a Standard Schema. + */ + type InferInput = NonNullable< + Schema["~standard"]["types"] + >["input"]; + /** + * Infers the output type of a Standard Schema. + */ + type InferOutput = NonNullable< + Schema["~standard"]["types"] + >["output"]; +} + +export type { v1 }; diff --git a/deno/lib/types.ts b/deno/lib/types.ts index 5d020d278..aec40ab71 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -23,6 +23,7 @@ import { import { partialUtil } from "./helpers/partialUtil.ts"; import { Primitive } from "./helpers/typeAliases.ts"; import { getParsedType, objectUtil, util, ZodParsedType } from "./helpers/util.ts"; +import { v1 } from "./standard-schema.ts"; import { IssueData, StringValidation, @@ -169,7 +170,8 @@ export abstract class ZodType< Output = any, Def extends ZodTypeDef = ZodTypeDef, Input = Output -> { +> implements v1.StandardSchema +{ readonly _type!: Output; readonly _output!: Output; readonly _input!: Input; @@ -179,6 +181,8 @@ export abstract class ZodType< return this._def.description; } + "~standard": v1.StandardSchemaProps; + abstract _parse(input: ParseInput): ParseReturnType; _getType(input: ParseInput): string { @@ -262,6 +266,53 @@ export abstract class ZodType< return handleResult(ctx, result); } + "~validate"( + data: unknown + ): v1.StandardResult | Promise> { + const ctx: ParseContext = { + common: { + issues: [], + async: !!(this["~standard"] as any).async, + }, + path: [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + + if (!(this["~standard"] as any).async) { + try { + const result = this._parseSync({ data, path: [], parent: ctx }); + return isValid(result) + ? { + value: result.value, + } + : { + issues: ctx.common.issues, + }; + } catch (err: any) { + if ((err as Error)?.message?.toLowerCase()?.includes("encountered")) { + (this["~standard"] as any).async = true; + } + (ctx as any).common = { + issues: [], + async: true, + }; + } + } + + return this._parseAsync({ data, path: [], parent: ctx }).then((result) => + isValid(result) + ? { + value: result.value, + } + : { + issues: ctx.common.issues, + } + ); + } + async parseAsync( data: unknown, params?: Partial @@ -422,6 +473,11 @@ export abstract class ZodType< this.readonly = this.readonly.bind(this); this.isNullable = this.isNullable.bind(this); this.isOptional = this.isOptional.bind(this); + this["~standard"] = { + version: 1, + vendor: "zod", + validate: (data) => this["~validate"](data), + }; } optional(): ZodOptional { diff --git a/package.json b/package.json index 639030673..628db24e1 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/preset-typescript": "^7.22.5", "@jest/globals": "^29.4.3", "@rollup/plugin-typescript": "^8.2.0", + "@standard-schema/spec": "^1.0.0-beta.3", "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", "@types/benchmark": "^2.1.0", @@ -59,14 +60,28 @@ "url": "https://github.com/colinhacks/zod/issues" }, "description": "TypeScript-first schema declaration and validation library with static type inference", - "files": ["/lib", "/index.d.ts"], + "files": [ + "/lib", + "/index.d.ts" + ], "funding": "https://github.com/sponsors/colinhacks", "homepage": "https://zod.dev", - "keywords": ["typescript", "schema", "validation", "type", "inference"], + "keywords": [ + "typescript", + "schema", + "validation", + "type", + "inference" + ], "license": "MIT", "lint-staged": { - "src/*.ts": ["eslint --cache --fix", "prettier --ignore-unknown --write"], - "*.md": ["prettier --ignore-unknown --write"] + "src/*.ts": [ + "eslint --cache --fix", + "prettier --ignore-unknown --write" + ], + "*.md": [ + "prettier --ignore-unknown --write" + ] }, "scripts": { "prettier:check": "prettier --check src/**/*.ts deno/lib/**/*.ts *.md --no-error-on-unmatched-pattern", diff --git a/src/__tests__/standard-schema.test.ts b/src/__tests__/standard-schema.test.ts new file mode 100644 index 000000000..08a4b3ca7 --- /dev/null +++ b/src/__tests__/standard-schema.test.ts @@ -0,0 +1,83 @@ +// @ts-ignore TS6133 +import { expect, test } from "@jest/globals"; +import { util } from "../helpers/util"; + +import * as z from "../index"; + +import type { v1 } from "@standard-schema/spec"; + +test("assignability", () => { + const _s1: v1.StandardSchema = z.string(); + const _s2: v1.StandardSchema = z.string(); + const _s3: v1.StandardSchema = z.string(); + const _s4: v1.StandardSchema = z.string(); + [_s1, _s2, _s3, _s4]; +}); + +test("type inference", () => { + const stringToNumber = z.string().transform((x) => x.length); + type input = v1.InferInput; + util.assertEqual(true); + type output = v1.InferOutput; + util.assertEqual(true); +}); + +test("valid parse", () => { + const schema = z.string(); + const result = schema["~standard"]["validate"]("hello"); + if (result instanceof Promise) { + throw new Error("Expected sync result"); + } + expect(result.issues).toEqual(undefined); + if (result.issues) { + throw new Error("Expected no issues"); + } else { + expect(result.value).toEqual("hello"); + } +}); + +test("invalid parse", () => { + const schema = z.string(); + const result = schema["~standard"]["validate"](1234); + if (result instanceof Promise) { + throw new Error("Expected sync result"); + } + expect(result.issues).toBeDefined(); + if (!result.issues) { + throw new Error("Expected issues"); + } + expect(result.issues.length).toEqual(1); + expect(result.issues[0].path).toEqual([]); +}); + +test("valid parse async", async () => { + const schema = z.string().refine(async () => true); + const _result = schema["~standard"]["validate"]("hello"); + if (_result instanceof Promise) { + const result = await _result; + expect(result.issues).toEqual(undefined); + if (result.issues) { + throw new Error("Expected no issues"); + } else { + expect(result.value).toEqual("hello"); + } + } else { + throw new Error("Expected async result"); + } +}); + +test("invalid parse async", async () => { + const schema = z.string().refine(async () => false); + const _result = schema["~standard"]["validate"]("hello"); + if (_result instanceof Promise) { + const result = await _result; + expect(result.issues).toBeDefined(); + if (!result.issues) { + throw new Error("Expected issues"); + } + expect(result.issues.length).toEqual(1); + expect(result.issues[0].path).toEqual([]); + } else { + throw new Error("Expected async result"); + } +}); diff --git a/src/standard-schema.ts b/src/standard-schema.ts new file mode 100644 index 000000000..a946c1b23 --- /dev/null +++ b/src/standard-schema.ts @@ -0,0 +1,113 @@ +declare namespace v1 { + /** + * The Standard Schema interface. + */ + interface StandardSchema { + /** + * The Standard Schema properties. + */ + readonly "~standard": StandardSchemaProps; + } + /** + * The Standard Schema properties interface. + */ + interface StandardSchemaProps { + /** + * The version number of the standard. + */ + readonly version: 1; + /** + * The vendor name of the schema library. + */ + readonly vendor: string; + /** + * Validates unknown input values. + */ + readonly validate: ( + value: unknown + ) => StandardResult | Promise>; + /** + * Inferred types associated with the schema. + */ + readonly types?: StandardTypes | undefined; + } + /** + * The result interface of the validate function. + */ + type StandardResult = + | StandardSuccessResult + | StandardFailureResult; + /** + * The result interface if validation succeeds. + */ + interface StandardSuccessResult { + /** + * The typed output value. + */ + readonly value: Output; + /** + * The non-existent issues. + */ + readonly issues?: undefined; + } + /** + * The result interface if validation fails. + */ + interface StandardFailureResult { + /** + * The issues of failed validation. + */ + readonly issues: ReadonlyArray; + } + /** + * The issue interface of the failure output. + */ + interface StandardIssue { + /** + * The error message of the issue. + */ + readonly message: string; + /** + * The path of the issue, if any. + */ + readonly path?: + | ReadonlyArray + | undefined; + } + /** + * The path segment interface of the issue. + */ + interface StandardPathSegment { + /** + * The key representing a path segment. + */ + readonly key: PropertyKey; + } + /** + * The base types interface of Standard Schema. + */ + interface StandardTypes { + /** + * The input type of the schema. + */ + readonly input: Input; + /** + * The output type of the schema. + */ + readonly output: Output; + } + /** + * Infers the input type of a Standard Schema. + */ + type InferInput = NonNullable< + Schema["~standard"]["types"] + >["input"]; + /** + * Infers the output type of a Standard Schema. + */ + type InferOutput = NonNullable< + Schema["~standard"]["types"] + >["output"]; +} + +export type { v1 }; diff --git a/src/types.ts b/src/types.ts index f3730ae14..a7316e001 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,7 @@ import { import { partialUtil } from "./helpers/partialUtil"; import { Primitive } from "./helpers/typeAliases"; import { getParsedType, objectUtil, util, ZodParsedType } from "./helpers/util"; +import { v1 } from "./standard-schema"; import { IssueData, StringValidation, @@ -169,7 +170,8 @@ export abstract class ZodType< Output = any, Def extends ZodTypeDef = ZodTypeDef, Input = Output -> { +> implements v1.StandardSchema +{ readonly _type!: Output; readonly _output!: Output; readonly _input!: Input; @@ -179,6 +181,8 @@ export abstract class ZodType< return this._def.description; } + "~standard": v1.StandardSchemaProps; + abstract _parse(input: ParseInput): ParseReturnType; _getType(input: ParseInput): string { @@ -262,6 +266,53 @@ export abstract class ZodType< return handleResult(ctx, result); } + "~validate"( + data: unknown + ): v1.StandardResult | Promise> { + const ctx: ParseContext = { + common: { + issues: [], + async: !!(this["~standard"] as any).async, + }, + path: [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data), + }; + + if (!(this["~standard"] as any).async) { + try { + const result = this._parseSync({ data, path: [], parent: ctx }); + return isValid(result) + ? { + value: result.value, + } + : { + issues: ctx.common.issues, + }; + } catch (err: any) { + if ((err as Error)?.message?.toLowerCase()?.includes("encountered")) { + (this["~standard"] as any).async = true; + } + (ctx as any).common = { + issues: [], + async: true, + }; + } + } + + return this._parseAsync({ data, path: [], parent: ctx }).then((result) => + isValid(result) + ? { + value: result.value, + } + : { + issues: ctx.common.issues, + } + ); + } + async parseAsync( data: unknown, params?: Partial @@ -422,6 +473,11 @@ export abstract class ZodType< this.readonly = this.readonly.bind(this); this.isNullable = this.isNullable.bind(this); this.isOptional = this.isOptional.bind(this); + this["~standard"] = { + version: 1, + vendor: "zod", + validate: (data) => this["~validate"](data), + }; } optional(): ZodOptional { diff --git a/yarn.lock b/yarn.lock index 5c18678b4..1f33e1e1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2881,6 +2881,11 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@standard-schema/spec@^1.0.0-beta.3": + version "1.0.0-beta.3" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0-beta.3.tgz#4dbf9a3c69e10fa2997e1ac1f39d8e9f33213576" + integrity sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw== + "@swc/core-darwin-arm64@1.4.8": version "1.4.8" resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.8.tgz#2fb702e209310c2da2bc712b0757c011b583a60d" From dff8fec535bd564afb95e9dd61e45ddf00437cbb Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Wed, 13 Nov 2024 16:11:08 -0800 Subject: [PATCH 2/6] Remove dep --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index 628db24e1..ded30130f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "@babel/preset-typescript": "^7.22.5", "@jest/globals": "^29.4.3", "@rollup/plugin-typescript": "^8.2.0", - "@standard-schema/spec": "^1.0.0-beta.3", "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", "@types/benchmark": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index 1f33e1e1e..5c18678b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2881,11 +2881,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@standard-schema/spec@^1.0.0-beta.3": - version "1.0.0-beta.3" - resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0-beta.3.tgz#4dbf9a3c69e10fa2997e1ac1f39d8e9f33213576" - integrity sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw== - "@swc/core-darwin-arm64@1.4.8": version "1.4.8" resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.8.tgz#2fb702e209310c2da2bc712b0757c011b583a60d" From 6afedaf968117fe8a83855b1473ef6a7d81f0be6 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Wed, 13 Nov 2024 17:28:35 -0800 Subject: [PATCH 3/6] WIP --- playground.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/playground.ts b/playground.ts index 4e01473b6..bca434a98 100644 --- a/playground.ts +++ b/playground.ts @@ -1,3 +1,16 @@ import { z } from "./src"; z; + +const schema = z + .string() + .transform((input) => input || undefined) + .optional() + .default("default"); + +type Input = z.input; // string | undefined +type Output = z.output; // string + +const result = schema.safeParse(""); + +console.log(result); // { success: true, data: undefined } From 775b337f8484bfcc1302ffac13eaeb877a9763e1 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Wed, 13 Nov 2024 17:50:30 -0800 Subject: [PATCH 4/6] Fix CI --- deno/lib/__tests__/standard-schema.test.ts | 2 +- src/__tests__/standard-schema.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deno/lib/__tests__/standard-schema.test.ts b/deno/lib/__tests__/standard-schema.test.ts index bb9497c9a..5c8eba96f 100644 --- a/deno/lib/__tests__/standard-schema.test.ts +++ b/deno/lib/__tests__/standard-schema.test.ts @@ -5,7 +5,7 @@ import { util } from "../helpers/util.ts"; import * as z from "../index.ts"; -import type { v1 } from "@standard-schema/spec"; +import type { v1 } from "../standard-schema.ts"; test("assignability", () => { const _s1: v1.StandardSchema = z.string(); diff --git a/src/__tests__/standard-schema.test.ts b/src/__tests__/standard-schema.test.ts index 08a4b3ca7..0d31bbc9b 100644 --- a/src/__tests__/standard-schema.test.ts +++ b/src/__tests__/standard-schema.test.ts @@ -4,7 +4,7 @@ import { util } from "../helpers/util"; import * as z from "../index"; -import type { v1 } from "@standard-schema/spec"; +import type { v1 } from "../standard-schema"; test("assignability", () => { const _s1: v1.StandardSchema = z.string(); From 501dbc2bf5883472b164bcad05a77ce924aa7717 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Mon, 9 Dec 2024 18:23:29 -0800 Subject: [PATCH 5/6] Update to latest standard-schema --- deno/lib/__tests__/standard-schema.test.ts | 14 ++--- deno/lib/standard-schema.ts | 62 ++++++++++++---------- deno/lib/types.ts | 10 ++-- src/__tests__/standard-schema.test.ts | 14 ++--- src/standard-schema.ts | 62 ++++++++++++---------- src/types.ts | 10 ++-- 6 files changed, 94 insertions(+), 78 deletions(-) diff --git a/deno/lib/__tests__/standard-schema.test.ts b/deno/lib/__tests__/standard-schema.test.ts index 5c8eba96f..77f89cbea 100644 --- a/deno/lib/__tests__/standard-schema.test.ts +++ b/deno/lib/__tests__/standard-schema.test.ts @@ -5,21 +5,21 @@ import { util } from "../helpers/util.ts"; import * as z from "../index.ts"; -import type { v1 } from "../standard-schema.ts"; +import type { StandardSchemaV1 } from "../standard-schema.ts"; test("assignability", () => { - const _s1: v1.StandardSchema = z.string(); - const _s2: v1.StandardSchema = z.string(); - const _s3: v1.StandardSchema = z.string(); - const _s4: v1.StandardSchema = z.string(); + const _s1: StandardSchemaV1 = z.string(); + const _s2: StandardSchemaV1 = z.string(); + const _s3: StandardSchemaV1 = z.string(); + const _s4: StandardSchemaV1 = z.string(); [_s1, _s2, _s3, _s4]; }); test("type inference", () => { const stringToNumber = z.string().transform((x) => x.length); - type input = v1.InferInput; + type input = StandardSchemaV1.InferInput; util.assertEqual(true); - type output = v1.InferOutput; + type output = StandardSchemaV1.InferOutput; util.assertEqual(true); }); diff --git a/deno/lib/standard-schema.ts b/deno/lib/standard-schema.ts index a946c1b23..111888e57 100644 --- a/deno/lib/standard-schema.ts +++ b/deno/lib/standard-schema.ts @@ -1,17 +1,18 @@ -declare namespace v1 { +/** + * The Standard Schema interface. + */ +export type StandardSchemaV1 = { /** - * The Standard Schema interface. + * The Standard Schema properties. */ - interface StandardSchema { - /** - * The Standard Schema properties. - */ - readonly "~standard": StandardSchemaProps; - } + readonly "~standard": StandardSchemaV1.Props; +}; + +export declare namespace StandardSchemaV1 { /** * The Standard Schema properties interface. */ - interface StandardSchemaProps { + export interface Props { /** * The version number of the standard. */ @@ -25,22 +26,22 @@ declare namespace v1 { */ readonly validate: ( value: unknown - ) => StandardResult | Promise>; + ) => Result | Promise>; /** * Inferred types associated with the schema. */ - readonly types?: StandardTypes | undefined; + readonly types?: Types | undefined; } + /** * The result interface of the validate function. */ - type StandardResult = - | StandardSuccessResult - | StandardFailureResult; + export type Result = SuccessResult | FailureResult; + /** * The result interface if validation succeeds. */ - interface StandardSuccessResult { + export interface SuccessResult { /** * The typed output value. */ @@ -50,19 +51,21 @@ declare namespace v1 { */ readonly issues?: undefined; } + /** * The result interface if validation fails. */ - interface StandardFailureResult { + export interface FailureResult { /** * The issues of failed validation. */ - readonly issues: ReadonlyArray; + readonly issues: ReadonlyArray; } + /** * The issue interface of the failure output. */ - interface StandardIssue { + export interface Issue { /** * The error message of the issue. */ @@ -70,23 +73,23 @@ declare namespace v1 { /** * The path of the issue, if any. */ - readonly path?: - | ReadonlyArray - | undefined; + readonly path?: ReadonlyArray | undefined; } + /** * The path segment interface of the issue. */ - interface StandardPathSegment { + export interface PathSegment { /** * The key representing a path segment. */ readonly key: PropertyKey; } + /** - * The base types interface of Standard Schema. + * The Standard Schema types interface. */ - interface StandardTypes { + export interface Types { /** * The input type of the schema. */ @@ -96,18 +99,21 @@ declare namespace v1 { */ readonly output: Output; } + /** * Infers the input type of a Standard Schema. */ - type InferInput = NonNullable< + export type InferInput = NonNullable< Schema["~standard"]["types"] >["input"]; + /** * Infers the output type of a Standard Schema. */ - type InferOutput = NonNullable< + export type InferOutput = NonNullable< Schema["~standard"]["types"] >["output"]; -} -export type { v1 }; + // biome-ignore lint/complexity/noUselessEmptyExport: needed for granular visibility control of TS namespace + export {}; +} diff --git a/deno/lib/types.ts b/deno/lib/types.ts index aec40ab71..fc3e8b21b 100644 --- a/deno/lib/types.ts +++ b/deno/lib/types.ts @@ -23,7 +23,7 @@ import { import { partialUtil } from "./helpers/partialUtil.ts"; import { Primitive } from "./helpers/typeAliases.ts"; import { getParsedType, objectUtil, util, ZodParsedType } from "./helpers/util.ts"; -import { v1 } from "./standard-schema.ts"; +import type { StandardSchemaV1 } from "./standard-schema.ts"; import { IssueData, StringValidation, @@ -170,7 +170,7 @@ export abstract class ZodType< Output = any, Def extends ZodTypeDef = ZodTypeDef, Input = Output -> implements v1.StandardSchema +> implements StandardSchemaV1 { readonly _type!: Output; readonly _output!: Output; @@ -181,7 +181,7 @@ export abstract class ZodType< return this._def.description; } - "~standard": v1.StandardSchemaProps; + "~standard": StandardSchemaV1.Props; abstract _parse(input: ParseInput): ParseReturnType; @@ -268,7 +268,9 @@ export abstract class ZodType< "~validate"( data: unknown - ): v1.StandardResult | Promise> { + ): + | StandardSchemaV1.Result + | Promise> { const ctx: ParseContext = { common: { issues: [], diff --git a/src/__tests__/standard-schema.test.ts b/src/__tests__/standard-schema.test.ts index 0d31bbc9b..1cf4c7ea4 100644 --- a/src/__tests__/standard-schema.test.ts +++ b/src/__tests__/standard-schema.test.ts @@ -4,21 +4,21 @@ import { util } from "../helpers/util"; import * as z from "../index"; -import type { v1 } from "../standard-schema"; +import type { StandardSchemaV1 } from "../standard-schema"; test("assignability", () => { - const _s1: v1.StandardSchema = z.string(); - const _s2: v1.StandardSchema = z.string(); - const _s3: v1.StandardSchema = z.string(); - const _s4: v1.StandardSchema = z.string(); + const _s1: StandardSchemaV1 = z.string(); + const _s2: StandardSchemaV1 = z.string(); + const _s3: StandardSchemaV1 = z.string(); + const _s4: StandardSchemaV1 = z.string(); [_s1, _s2, _s3, _s4]; }); test("type inference", () => { const stringToNumber = z.string().transform((x) => x.length); - type input = v1.InferInput; + type input = StandardSchemaV1.InferInput; util.assertEqual(true); - type output = v1.InferOutput; + type output = StandardSchemaV1.InferOutput; util.assertEqual(true); }); diff --git a/src/standard-schema.ts b/src/standard-schema.ts index a946c1b23..111888e57 100644 --- a/src/standard-schema.ts +++ b/src/standard-schema.ts @@ -1,17 +1,18 @@ -declare namespace v1 { +/** + * The Standard Schema interface. + */ +export type StandardSchemaV1 = { /** - * The Standard Schema interface. + * The Standard Schema properties. */ - interface StandardSchema { - /** - * The Standard Schema properties. - */ - readonly "~standard": StandardSchemaProps; - } + readonly "~standard": StandardSchemaV1.Props; +}; + +export declare namespace StandardSchemaV1 { /** * The Standard Schema properties interface. */ - interface StandardSchemaProps { + export interface Props { /** * The version number of the standard. */ @@ -25,22 +26,22 @@ declare namespace v1 { */ readonly validate: ( value: unknown - ) => StandardResult | Promise>; + ) => Result | Promise>; /** * Inferred types associated with the schema. */ - readonly types?: StandardTypes | undefined; + readonly types?: Types | undefined; } + /** * The result interface of the validate function. */ - type StandardResult = - | StandardSuccessResult - | StandardFailureResult; + export type Result = SuccessResult | FailureResult; + /** * The result interface if validation succeeds. */ - interface StandardSuccessResult { + export interface SuccessResult { /** * The typed output value. */ @@ -50,19 +51,21 @@ declare namespace v1 { */ readonly issues?: undefined; } + /** * The result interface if validation fails. */ - interface StandardFailureResult { + export interface FailureResult { /** * The issues of failed validation. */ - readonly issues: ReadonlyArray; + readonly issues: ReadonlyArray; } + /** * The issue interface of the failure output. */ - interface StandardIssue { + export interface Issue { /** * The error message of the issue. */ @@ -70,23 +73,23 @@ declare namespace v1 { /** * The path of the issue, if any. */ - readonly path?: - | ReadonlyArray - | undefined; + readonly path?: ReadonlyArray | undefined; } + /** * The path segment interface of the issue. */ - interface StandardPathSegment { + export interface PathSegment { /** * The key representing a path segment. */ readonly key: PropertyKey; } + /** - * The base types interface of Standard Schema. + * The Standard Schema types interface. */ - interface StandardTypes { + export interface Types { /** * The input type of the schema. */ @@ -96,18 +99,21 @@ declare namespace v1 { */ readonly output: Output; } + /** * Infers the input type of a Standard Schema. */ - type InferInput = NonNullable< + export type InferInput = NonNullable< Schema["~standard"]["types"] >["input"]; + /** * Infers the output type of a Standard Schema. */ - type InferOutput = NonNullable< + export type InferOutput = NonNullable< Schema["~standard"]["types"] >["output"]; -} -export type { v1 }; + // biome-ignore lint/complexity/noUselessEmptyExport: needed for granular visibility control of TS namespace + export {}; +} diff --git a/src/types.ts b/src/types.ts index a7316e001..b2cd5488a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,7 +23,7 @@ import { import { partialUtil } from "./helpers/partialUtil"; import { Primitive } from "./helpers/typeAliases"; import { getParsedType, objectUtil, util, ZodParsedType } from "./helpers/util"; -import { v1 } from "./standard-schema"; +import type { StandardSchemaV1 } from "./standard-schema"; import { IssueData, StringValidation, @@ -170,7 +170,7 @@ export abstract class ZodType< Output = any, Def extends ZodTypeDef = ZodTypeDef, Input = Output -> implements v1.StandardSchema +> implements StandardSchemaV1 { readonly _type!: Output; readonly _output!: Output; @@ -181,7 +181,7 @@ export abstract class ZodType< return this._def.description; } - "~standard": v1.StandardSchemaProps; + "~standard": StandardSchemaV1.Props; abstract _parse(input: ParseInput): ParseReturnType; @@ -268,7 +268,9 @@ export abstract class ZodType< "~validate"( data: unknown - ): v1.StandardResult | Promise> { + ): + | StandardSchemaV1.Result + | Promise> { const ctx: ParseContext = { common: { issues: [], From b3434ed0eb09ae081e8c5e0c83c926b658ec0115 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Mon, 9 Dec 2024 18:24:27 -0800 Subject: [PATCH 6/6] Add standard-schema/spec as devDep --- deno/lib/__tests__/standard-schema.test.ts | 2 +- package.json | 1 + src/__tests__/standard-schema.test.ts | 2 +- yarn.lock | 5 +++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/deno/lib/__tests__/standard-schema.test.ts b/deno/lib/__tests__/standard-schema.test.ts index 77f89cbea..065315c52 100644 --- a/deno/lib/__tests__/standard-schema.test.ts +++ b/deno/lib/__tests__/standard-schema.test.ts @@ -5,7 +5,7 @@ import { util } from "../helpers/util.ts"; import * as z from "../index.ts"; -import type { StandardSchemaV1 } from "../standard-schema.ts"; +import type { StandardSchemaV1 } from "@standard-schema/spec"; test("assignability", () => { const _s1: StandardSchemaV1 = z.string(); diff --git a/package.json b/package.json index ded30130f..d6ecb8918 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/preset-typescript": "^7.22.5", "@jest/globals": "^29.4.3", "@rollup/plugin-typescript": "^8.2.0", + "@standard-schema/spec": "^1.0.0-beta.4", "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", "@types/benchmark": "^2.1.0", diff --git a/src/__tests__/standard-schema.test.ts b/src/__tests__/standard-schema.test.ts index 1cf4c7ea4..8f67cce76 100644 --- a/src/__tests__/standard-schema.test.ts +++ b/src/__tests__/standard-schema.test.ts @@ -4,7 +4,7 @@ import { util } from "../helpers/util"; import * as z from "../index"; -import type { StandardSchemaV1 } from "../standard-schema"; +import type { StandardSchemaV1 } from "@standard-schema/spec"; test("assignability", () => { const _s1: StandardSchemaV1 = z.string(); diff --git a/yarn.lock b/yarn.lock index 5c18678b4..2016a4c07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2881,6 +2881,11 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@standard-schema/spec@^1.0.0-beta.4": + version "1.0.0-beta.4" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0-beta.4.tgz#62f520109add3eb016004098363bfee0678dd1ec" + integrity sha512-d3IxtzLo7P1oZ8s8YNvxzBUXRXojSut8pbPrTYtzsc5sn4+53jVqbk66pQerSZbZSJZQux6LkclB/+8IDordHg== + "@swc/core-darwin-arm64@1.4.8": version "1.4.8" resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.8.tgz#2fb702e209310c2da2bc712b0757c011b583a60d"