From e2389b0312fb1b280a90d4bd1f10d099178aec9a Mon Sep 17 00:00:00 2001
From: Colin McDonnell <colinmcd@alum.mit.edu>
Date: Sun, 24 Oct 2021 14:16:04 -0400
Subject: [PATCH] Add describe

---
 README.md                              |  8 ++++++--
 coverage.svg                           |  2 +-
 deno/lib/__tests__/description.test.ts | 11 +++++++++++
 deno/lib/types.ts                      | 16 +++++++++++++++-
 package.json                           |  2 +-
 src/__tests__/description.test.ts      | 10 ++++++++++
 src/types.ts                           | 16 +++++++++++++++-
 7 files changed, 59 insertions(+), 6 deletions(-)
 create mode 100644 deno/lib/__tests__/description.test.ts
 create mode 100644 src/__tests__/description.test.ts

diff --git a/README.md b/README.md
index 3ef8b5058..696b94386 100644
--- a/README.md
+++ b/README.md
@@ -11,10 +11,14 @@
 
 </p>
 <p align="center">
-⭐️ smash that star button ⭐️
+by [@colinhacks](https://twitter.com/colinhacks)
 </p>
 
-> Like typesafety? Check out [tRPC](https://trpc.io) — a better way to build end-to-end typesafe APIs without GraphQL or code generation — just TypeScript.
+> Hi! Colin here, creator of Zod. I hope you find it easy to use and powerful enough for all your use cases. If you have any issues or suggestions, please  [open an issue](https://github.com/colinhacks/zod/issues/new)! 
+>
+> If you like typesafety, check out my other library [tRPC](https://trpc.io). It works in concert with Zod to provide a seamless way to build end-to-end typesafe APIs without GraphQL or code generation — just TypeScript.
+>
+> Colin (AKA [@colinhacks](https://twitter.com/colinhacks))
 
 <br/>
 
diff --git a/coverage.svg b/coverage.svg
index d77838c30..297acbbf9 100644
--- a/coverage.svg
+++ b/coverage.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20" role="img" aria-label="Coverage: 95.79%"><title>Coverage: 95.79%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#4c1"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">95.79%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">95.79%</text></g></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="110" height="20" role="img" aria-label="Coverage: 95.8%"><title>Coverage: 95.8%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="110" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="47" height="20" fill="#4c1"/><rect width="110" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="855" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="370">95.8%</text><text x="855" y="140" transform="scale(.1)" fill="#fff" textLength="370">95.8%</text></g></svg>
\ No newline at end of file
diff --git a/deno/lib/__tests__/description.test.ts b/deno/lib/__tests__/description.test.ts
new file mode 100644
index 000000000..d10e60b61
--- /dev/null
+++ b/deno/lib/__tests__/description.test.ts
@@ -0,0 +1,11 @@
+// @ts-ignore TS6133
+import { expect } from "https://deno.land/x/expect@v0.2.6/mod.ts";
+const test = Deno.test;
+
+import * as z from "../index.ts";
+
+test("description", () => {
+  const schema: any = z.string();
+  const DESC = "asdlfkjasdf";
+  expect(schema.describe(DESC).description).toEqual(DESC);
+});
diff --git a/deno/lib/types.ts b/deno/lib/types.ts
index 889723997..0b889fd39 100644
--- a/deno/lib/types.ts
+++ b/deno/lib/types.ts
@@ -54,6 +54,7 @@ export type { TypeOf as infer };
 export type CustomErrorParams = Partial<util.Omit<ZodCustomIssue, "code">>;
 export interface ZodTypeDef {
   errorMap?: ZodErrorMap;
+  description?: string;
 }
 
 const handleResult = <Input, Output>(
@@ -78,9 +79,10 @@ type RawCreateParams =
       errorMap?: ZodErrorMap;
       invalid_type_error?: string;
       required_error?: string;
+      description?: string;
     }
   | undefined;
-type ProcessedCreateParams = { errorMap?: ZodErrorMap };
+type ProcessedCreateParams = { errorMap?: ZodErrorMap; description?: string };
 function processCreateParams(params: RawCreateParams): ProcessedCreateParams {
   if (!params) return {};
   if (params.errorMap && (params.invalid_type_error || params.required_error)) {
@@ -110,6 +112,10 @@ export abstract class ZodType<
   readonly _input!: Input;
   readonly _def!: Def;
 
+  get description() {
+    return this._def.description;
+  }
+
   abstract _parse(input: ParseInput): ParseReturnType<Output>;
 
   _processInputParams(
@@ -336,6 +342,14 @@ export abstract class ZodType<
     }) as any;
   }
 
+  describe(description: string): this {
+    const This = (this as any).constructor;
+    return new This({
+      ...this._def,
+      description,
+    });
+  }
+
   isOptional(): boolean {
     return this.safeParse(undefined).success;
   }
diff --git a/package.json b/package.json
index a9b4f151b..bc3f3b04a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "zod",
-  "version": "3.11.2",
+  "version": "3.11.4",
   "description": "TypeScript-first schema declaration and validation library with static type inference",
   "main": "./lib/index.js",
   "types": "./lib/index.d.ts",
diff --git a/src/__tests__/description.test.ts b/src/__tests__/description.test.ts
new file mode 100644
index 000000000..9c2f7286c
--- /dev/null
+++ b/src/__tests__/description.test.ts
@@ -0,0 +1,10 @@
+// @ts-ignore TS6133
+import { expect, test } from "@jest/globals";
+
+import * as z from "../index";
+
+test("description", () => {
+  const schema: any = z.string();
+  const DESC = "asdlfkjasdf";
+  expect(schema.describe(DESC).description).toEqual(DESC);
+});
diff --git a/src/types.ts b/src/types.ts
index be5a030b0..020eaccec 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -54,6 +54,7 @@ export type { TypeOf as infer };
 export type CustomErrorParams = Partial<util.Omit<ZodCustomIssue, "code">>;
 export interface ZodTypeDef {
   errorMap?: ZodErrorMap;
+  description?: string;
 }
 
 const handleResult = <Input, Output>(
@@ -78,9 +79,10 @@ type RawCreateParams =
       errorMap?: ZodErrorMap;
       invalid_type_error?: string;
       required_error?: string;
+      description?: string;
     }
   | undefined;
-type ProcessedCreateParams = { errorMap?: ZodErrorMap };
+type ProcessedCreateParams = { errorMap?: ZodErrorMap; description?: string };
 function processCreateParams(params: RawCreateParams): ProcessedCreateParams {
   if (!params) return {};
   if (params.errorMap && (params.invalid_type_error || params.required_error)) {
@@ -110,6 +112,10 @@ export abstract class ZodType<
   readonly _input!: Input;
   readonly _def!: Def;
 
+  get description() {
+    return this._def.description;
+  }
+
   abstract _parse(input: ParseInput): ParseReturnType<Output>;
 
   _processInputParams(
@@ -336,6 +342,14 @@ export abstract class ZodType<
     }) as any;
   }
 
+  describe(description: string): this {
+    const This = (this as any).constructor;
+    return new This({
+      ...this._def,
+      description,
+    });
+  }
+
   isOptional(): boolean {
     return this.safeParse(undefined).success;
   }