Skip to content

Commit

Permalink
fix: optional fields are not required and fix stack overflow on extend (
Browse files Browse the repository at this point in the history
#23)

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

* chore: setuo github ci to build and test

* fix last step
  • Loading branch information
sam-goodwin authored Mar 30, 2024
1 parent 238f34b commit 5bea2da
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 44 deletions.
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;
};

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

0 comments on commit 5bea2da

Please sign in to comment.