Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: optional fields are not required and fix stack overflow on extend #23

Merged
merged 3 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on: [pull_request]

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '20'
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run build
- name: Test
run: pnpm run test
- name: Compile tests
run: pnpm tsc --project tsconfig.test.json
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,22 @@
"test": "NODE_OPTIONS=--experimental-vm-modules jest && cd ./test/pkg && pnpm i && pnpm build",
"watch": "tsc -b -w"
},
"dependencies": {
"type-fest": "^4.14.0"
},
"peerDependencies": {
"zod": "^3"
},
"devDependencies": {
"@tsconfig/node16": "^1.0.3",
"@types/jest": "^29.5.4",
"@types/node": "^16",
"bun": "^1.0.36",
"jest": "^29.7.0",
"prettier": "^2.8.8",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
"typescript": "^5.4.3",
"zod": "3.20.2"
},
"dependencies": {
"type-fest": "^4.6.0"
}
}
100 changes: 85 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 11 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { PascalCase } from "type-fest";
import type { PascalCase } from "type-fest";

import {
ParseInput,
ParseParams,
ParseReturnType,
RefinementCtx,
SafeParseReturnType,
SyncParseReturnType,
ZodArray,
ZodEffects,
ZodFunction,
ZodIntersection,
ZodLazy,
Expand All @@ -25,14 +23,16 @@ import {
ZodTypeAny,
ZodUnion,
object,
z,
z,
} from "zod";

import { toPascalCase } from "./to-pascal-case.js";
import { isPromise } from "util/types";
import { isPromise } from "node:util/types";

const IS_ZOD_CLASS = Symbol.for("zod-class");

type Ctor<T = any> = {
[key: string]: any;
new (input: any): T;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, i did this for debugging. Can be removed.

};

Expand All @@ -42,6 +42,7 @@ export interface ZodClass<
Shape extends ZodRawShape = ZodRawShape
> extends ZodType<Instance> {
shape: Shape;
staticProps: StaticProperties<Shape>;

pick<Mask extends keyof Shape>(...mask: Mask[]): Z.Class<Pick<Shape, Mask>>;
pick<
Expand Down Expand Up @@ -82,9 +83,9 @@ export interface ZodClass<
extend<Super extends Ctor, ChildShape extends ZodRawShape>(
this: Super,
shape: ChildShape
): StaticProperties<ChildShape> & {
[k in keyof Super]: Super[k];
} & ZodClass<
): StaticProperties<ChildShape> & ({
[k in Exclude<keyof Super, keyof z.ZodObject<any>>]: Super[k];
}) & ZodClass<
Z.infer<ZodObject<ChildShape>> & ConstructorParameters<Super>[0],
Z.infer<ZodObject<ChildShape>> & InstanceType<Super>,
Omit<Shape, keyof ChildShape> & ChildShape
Expand Down Expand Up @@ -146,7 +147,7 @@ export declare namespace Z {
? { [i in keyof T]: Z.infer<T[i]> }
: T extends ZodRecord<infer Key, infer Value>
? {
[k in Z.infer<Key>]: Z.infer<Value>;
[k in Extract<Z.infer<Key>, string | number | symbol>]: Z.infer<Value>;
}
: T extends ZodMap<infer Key, infer Value>
? Map<Z.infer<Key>, Z.infer<Value>>
Expand All @@ -167,10 +168,6 @@ type StaticProperties<Shape extends ZodRawShape> = {
[property in keyof Shape as PascalCase<property>]: Shape[property];
};

export interface Z {
class<Shape extends ZodRawShape>(shape: Shape): Z.Class<Shape>;
}

export declare namespace Z {
export type Class<Shape extends ZodRawShape> = StaticProperties<Shape> &
ZodClass<Z.infer<ZodObject<Shape>>, Z.infer<ZodObject<Shape>>, Shape>;
Expand All @@ -179,15 +176,7 @@ export declare namespace Z {
export const Z = {
class<T extends ZodRawShape>(
shape: T
): {
[property in keyof T as PascalCase<property>]: T[property];
} & ZodClass<
{
[k in keyof T]: Z.infer<T[k]>;
},
Z.infer<ZodObject<T>>,
T
> {
): Z.Class<T> {
const clazz = class {
static [IS_ZOD_CLASS]: true = true;
static schema() {
Expand Down
6 changes: 3 additions & 3 deletions test/pkg/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions test/pkg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Z } from "zod-class";
class User extends Z.class({
name: z.string(),
age: z.number(),
dob: z.date().optional()
}) {
getName() {
return this.name;
Expand All @@ -13,3 +14,14 @@ class User extends Z.class({
const user = User.parse({ name: "John", age: 20 });

user.getName();

new User({
name: "John",
age: 20,
});

new User({
name: "John",
age: 20,
dob: new Date(),
});
14 changes: 14 additions & 0 deletions test/zod-class.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ test("static methods should be inherited", () => {
return "foo";
}
}

class Bar extends Foo.extend({
bar: z.number(),
}) {
Expand Down Expand Up @@ -226,6 +227,9 @@ test("static properties should plumb through", () => {
class Bar extends Foo.extend({
bar: z.number(),
}) {}
Foo.staticProps

// type Keys = A extends (new (...args: any[]) => any) & infer Rest ? keyof Rest : never;

Foo.Id;
Bar.Id;
Expand Down Expand Up @@ -455,3 +459,13 @@ test("z.union([User, Person])", () => {
expect(user).toBeInstanceOf(User);
expect(person).toBeInstanceOf(Person);
});

test("optional object fields are optional", () => {
// see: https://github.com/sam-goodwin/zod-class/issues/22
class drat extends Z.class({
foo: z.string().optional(),
}) {}

const ab = {};
new drat(ab);
})
Loading
Loading