Skip to content

Commit

Permalink
Improve type inference in generics
Browse files Browse the repository at this point in the history
  • Loading branch information
colinhacks committed Mar 6, 2023
1 parent b276d71 commit 4d016b7
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 71 deletions.
28 changes: 28 additions & 0 deletions deno/lib/__tests__/generics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// @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";

test("generics", () => {
async function stripOuter<TData extends z.ZodTypeAny>(
schema: TData,
url: string
): Promise<TData["_output"]> {
const zStrippedResponse = z
.object({
topLevelKey: schema,
})
.transform((data) => {
return data.topLevelKey;
});

return fetch(url)
.then((response) => response.json())
.then((data) => zStrippedResponse.parse(data));
}

const result = stripOuter(z.number(), "");
util.assertEqual<typeof result, Promise<number>>(true);
});
50 changes: 24 additions & 26 deletions deno/lib/helpers/partialUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
ZodNullable,
ZodObject,
ZodOptional,
ZodRawShape,
ZodTuple,
ZodTupleItems,
ZodTypeAny,
Expand Down Expand Up @@ -34,33 +35,30 @@ export namespace partialUtil {
// ? "object" // T extends ZodOptional<any> // ? 'optional' // :
// : "rest"];

export type DeepPartial<T extends ZodTypeAny> = T extends ZodObject<
infer Shape,
infer Params,
infer Catchall
>
? ZodObject<
{ [k in keyof Shape]: ZodOptional<DeepPartial<Shape[k]>> },
Params,
Catchall
>
: T extends ZodArray<infer Type, infer Card>
? ZodArray<DeepPartial<Type>, Card>
: T extends ZodOptional<infer Type>
? ZodOptional<DeepPartial<Type>>
: T extends ZodNullable<infer Type>
? ZodNullable<DeepPartial<Type>>
: T extends ZodTuple<infer Items>
? {
[k in keyof Items]: Items[k] extends ZodTypeAny
? DeepPartial<Items[k]>
: never;
} extends infer PI
? PI extends ZodTupleItems
? ZodTuple<PI>
export type DeepPartial<T extends ZodTypeAny> =
T extends ZodObject<ZodRawShape>
? ZodObject<
{ [k in keyof T["shape"]]: ZodOptional<DeepPartial<T["shape"][k]>> },
T["_def"]["unknownKeys"],
T["_def"]["catchall"]
>
: T extends ZodArray<infer Type, infer Card>
? ZodArray<DeepPartial<Type>, Card>
: T extends ZodOptional<infer Type>
? ZodOptional<DeepPartial<Type>>
: T extends ZodNullable<infer Type>
? ZodNullable<DeepPartial<Type>>
: T extends ZodTuple<infer Items>
? {
[k in keyof Items]: Items[k] extends ZodTypeAny
? DeepPartial<Items[k]>
: never;
} extends infer PI
? PI extends ZodTupleItems
? ZodTuple<PI>
: never
: never
: never
: T;
: T;
// {
// // optional: T extends ZodOptional<ZodTypeAny> ? T : ZodOptional<T>;
// // array: T extends ZodArray<infer Type> ? ZodArray<DeepPartial<Type>> : never;
Expand Down
20 changes: 1 addition & 19 deletions playground.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,2 @@
import { z } from "./src";

async function stripOuter<TData extends z.ZodTypeAny>(
schema: TData,
url: string
): Promise<TData["_output"]> {
const zStrippedResponse = z
.object({
topLevelKey: schema,
})
.transform((data) => {
return data.topLevelKey;
});

return fetch(url)
.then((response) => response.json())
.then((data) => zStrippedResponse.parse(data));
}

type asdf = Omit<{ a: string }, "b">;
z;
27 changes: 27 additions & 0 deletions src/__tests__/generics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @ts-ignore TS6133
import { expect, test } from "@jest/globals";

import { util } from "../helpers/util";
import * as z from "../index";

test("generics", () => {
async function stripOuter<TData extends z.ZodTypeAny>(
schema: TData,
url: string
): Promise<TData["_output"]> {
const zStrippedResponse = z
.object({
topLevelKey: schema,
})
.transform((data) => {
return data.topLevelKey;
});

return fetch(url)
.then((response) => response.json())
.then((data) => zStrippedResponse.parse(data));
}

const result = stripOuter(z.number(), "");
util.assertEqual<typeof result, Promise<number>>(true);
});
50 changes: 24 additions & 26 deletions src/helpers/partialUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
ZodNullable,
ZodObject,
ZodOptional,
ZodRawShape,
ZodTuple,
ZodTupleItems,
ZodTypeAny,
Expand Down Expand Up @@ -34,33 +35,30 @@ export namespace partialUtil {
// ? "object" // T extends ZodOptional<any> // ? 'optional' // :
// : "rest"];

export type DeepPartial<T extends ZodTypeAny> = T extends ZodObject<
infer Shape,
infer Params,
infer Catchall
>
? ZodObject<
{ [k in keyof Shape]: ZodOptional<DeepPartial<Shape[k]>> },
Params,
Catchall
>
: T extends ZodArray<infer Type, infer Card>
? ZodArray<DeepPartial<Type>, Card>
: T extends ZodOptional<infer Type>
? ZodOptional<DeepPartial<Type>>
: T extends ZodNullable<infer Type>
? ZodNullable<DeepPartial<Type>>
: T extends ZodTuple<infer Items>
? {
[k in keyof Items]: Items[k] extends ZodTypeAny
? DeepPartial<Items[k]>
: never;
} extends infer PI
? PI extends ZodTupleItems
? ZodTuple<PI>
export type DeepPartial<T extends ZodTypeAny> =
T extends ZodObject<ZodRawShape>
? ZodObject<
{ [k in keyof T["shape"]]: ZodOptional<DeepPartial<T["shape"][k]>> },
T["_def"]["unknownKeys"],
T["_def"]["catchall"]
>
: T extends ZodArray<infer Type, infer Card>
? ZodArray<DeepPartial<Type>, Card>
: T extends ZodOptional<infer Type>
? ZodOptional<DeepPartial<Type>>
: T extends ZodNullable<infer Type>
? ZodNullable<DeepPartial<Type>>
: T extends ZodTuple<infer Items>
? {
[k in keyof Items]: Items[k] extends ZodTypeAny
? DeepPartial<Items[k]>
: never;
} extends infer PI
? PI extends ZodTupleItems
? ZodTuple<PI>
: never
: never
: never
: T;
: T;
// {
// // optional: T extends ZodOptional<ZodTypeAny> ? T : ZodOptional<T>;
// // array: T extends ZodArray<infer Type> ? ZodArray<DeepPartial<Type>> : never;
Expand Down

0 comments on commit 4d016b7

Please sign in to comment.