zod-compare 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/src/__tests__/is-compatible-type.test.ts +136 -0
- package/src/__tests__/is-same-type.test.ts +174 -0
- package/src/index.ts +3 -0
- package/src/is-compatible-type.ts +125 -0
- package/src/is-same-type.ts +208 -0
- package/src/utils.ts +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Zod Compare
|
|
2
|
+
|
|
3
|
+
[](https://github.com/lawvs/zod-compare/actions/workflows/build.yml)
|
|
4
|
+
|
|
5
|
+
Compare two [Zod](https://zod.dev/) schemas recursively.
|
|
6
|
+
|
|
7
|
+
`zod-compare` provides functions to compare Zod schemas, allowing you to determine whether two schemas are the same or compatible.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install zod zod-compare --save
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { isSameType, isCompatibleType } from "zod-compare";
|
|
20
|
+
|
|
21
|
+
isSameType(z.string(), z.string()); // true
|
|
22
|
+
isSameType(z.string(), z.number()); // false
|
|
23
|
+
isSameType(
|
|
24
|
+
z.object({ name: z.string(), other: z.number() }),
|
|
25
|
+
z.object({ name: z.string() }),
|
|
26
|
+
);
|
|
27
|
+
// false
|
|
28
|
+
|
|
29
|
+
isCompatibleType(
|
|
30
|
+
z.object({ name: z.string(), other: z.number() }),
|
|
31
|
+
z.object({ name: z.string() }),
|
|
32
|
+
);
|
|
33
|
+
// true
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Advanced Usage
|
|
37
|
+
|
|
38
|
+
`isSameType` also offers an `interceptor` option, enabling you to tailor the comparison process according to your needs.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
isSameType(
|
|
42
|
+
z.object({ name: z.string(), other: z.number() }),
|
|
43
|
+
z.object({ name: z.string() }),
|
|
44
|
+
{
|
|
45
|
+
interceptor: (a, b) => {
|
|
46
|
+
if (a instanceof z.ZodObject && b instanceof z.ZodObject) {
|
|
47
|
+
// return boolean to override the comparison result
|
|
48
|
+
return a.shape.name === b.shape.name;
|
|
49
|
+
}
|
|
50
|
+
// return other values to use the default comparison
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
// true
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Caveats
|
|
58
|
+
|
|
59
|
+
`zod-compare` is designed to compare Zod schemas created by the developer.
|
|
60
|
+
We do not have plans to support comparing schemas that include custom validation functions or other advanced Zod features.
|
|
61
|
+
|
|
62
|
+
This tool will disregard any custom validations like `min`, `max`, `length`, among others.
|
|
63
|
+
|
|
64
|
+
Furthermore, it cannot be utilized for comparing `ZodLazy`, `ZodEffects`, `ZodDefault`, `ZodCatch`, `ZodPipeline`, `ZodTransformer`, `ZodError` types.
|
|
65
|
+
|
|
66
|
+
But you can use the `interceptor` option to customize the comparison process.
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
### `isSameType`
|
|
71
|
+
|
|
72
|
+
Compares two Zod schemas and returns `true` if they are the same.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
function isSameType(
|
|
76
|
+
a: ZodType,
|
|
77
|
+
b: ZodType,
|
|
78
|
+
options?: {
|
|
79
|
+
interceptor?: (a: ZodType, b: ZodType) => boolean | undefined;
|
|
80
|
+
},
|
|
81
|
+
): boolean;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `isCompatibleType` (Unstable API)
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
function isCompatibleType(
|
|
88
|
+
a: ZodType,
|
|
89
|
+
b: ZodType,
|
|
90
|
+
options?: {
|
|
91
|
+
interceptor?: (a: ZodType, b: ZodType) => boolean | undefined;
|
|
92
|
+
},
|
|
93
|
+
): boolean;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("zod"),a={deep:!0,ignoreOptional:!1,ignoreNullable:!1,ignoreReadOnly:!1,ignoreBranded:!1,ignoreValidations:!0,ignoreIntersectionsOrder:!1,interceptor:()=>{}},l=e=>e instanceof t.z.ZodString||e instanceof t.z.ZodNumber||e instanceof t.z.ZodNaN||e instanceof t.z.ZodBigInt||e instanceof t.z.ZodBoolean||e instanceof t.z.ZodDate||e instanceof t.z.ZodSymbol||e instanceof t.z.ZodUndefined||e instanceof t.z.ZodNull||e instanceof t.z.ZodAny||e instanceof t.z.ZodUnknown||e instanceof t.z.ZodNever||e instanceof t.z.ZodVoid,f=(e,n,d)=>{const r={...a,...d},u=r.interceptor(e,n,r);if(u===!0||u===!1)return u;if(e===void 0||n===void 0)throw new Error("Failed to compare type! "+e+" "+n);if(e===n)return!0;if(e.constructor!==n.constructor)return!1;if(!("typeName"in e._def)||!("typeName"in n._def))throw new Error("Failed to compare type! "+e._def+" "+n._def);if(e._def.typeName!==n._def.typeName)return!1;if(r.ignoreBranded)return e instanceof t.z.ZodBranded&&(e=e.unwrap()),n instanceof t.z.ZodBranded&&(n=n.unwrap()),f(e,n,r);if(e instanceof t.z.ZodBranded||n instanceof t.z.ZodBranded)return!1;if("unwrap"in e&&typeof e.unwrap=="function")return"unwrap"in n&&typeof n.unwrap=="function"?f(e.unwrap(),n.unwrap(),r):!1;if(!r.ignoreOptional&&e.isOptional()!==n.isOptional()||!r.ignoreNullable&&e.isNullable()!==n.isNullable())return!1;if(l(e))return!0;if(e instanceof t.z.ZodObject&&n instanceof t.z.ZodObject){const o=e.shape,s=n.shape;if(Object.keys(o).length!==Object.keys(s).length)return!1;for(const i in o)if(!(i in s)||!f(o[i],s[i],r))return!1;return!0}if(e instanceof t.z.ZodArray&&n instanceof t.z.ZodArray)return f(e.element,n.element,r);if(e instanceof t.z.ZodTuple&&n instanceof t.z.ZodTuple){if(e.items.length!==n.items.length)return!1;for(let o=0;o<e.items.length;o++)if(!f(e.items[o],n.items[o],r))return!1;return e._def.rest||n._def.rest?!e._def.rest||!n._def.rest?!1:f(e._def.rest,n._def.rest,r):!0}if(e instanceof t.z.ZodLiteral&&n instanceof t.z.ZodLiteral)return e.value===n.value;if(e instanceof t.z.ZodIntersection&&n instanceof t.z.ZodIntersection)return f(e._def.left,n._def.left,r)&&f(e._def.right,n._def.right,r);if(e instanceof t.z.ZodUnion&&n instanceof t.z.ZodUnion){if(e.options.length!==n.options.length)return!1;for(let o=0;o<e.options.length;o++)if(!f(e.options[o],n.options[o],r))return!1;return!0}if(e instanceof t.z.ZodReadonly&&n instanceof t.z.ZodReadonly)return f(e._def.innerType,n._def.innerType,r);if(e instanceof t.z.ZodRecord&&n instanceof t.z.ZodRecord||e instanceof t.z.ZodMap&&n instanceof t.z.ZodMap)return f(e.keySchema,n.keySchema,r)&&f(e.valueSchema,n.valueSchema,r);if(e instanceof t.z.ZodSet&&n instanceof t.z.ZodSet)return f(e._def.valueType,n._def.valueType,r);if(e instanceof t.z.ZodFunction&&n instanceof t.z.ZodFunction)return f(e.parameters(),n.parameters(),r)&&f(e.returnType(),n.returnType(),r);if(e instanceof t.z.ZodEnum&&n instanceof t.z.ZodEnum){const o=e.options,s=n.options;if(o.length!==s.length)return!1;for(let i=0;i<o.length;i++)if(o[i]!==s[i])return!1;return!0}if(e instanceof t.z.ZodNativeEnum&&n instanceof t.z.ZodNativeEnum){const o=e.enum,s=n.enum;if(Object.keys(o).length!==Object.keys(s).length)return!1;for(const i in o)if(o[i]!==s[i])return!1;return!0}throw console.error('Failed to compare type! "'+e,n),new Error("Unknown type! "+e._def.typeName+" "+n._def.typeName)},c=(e,n,d)=>{const r={...a,...d},u=r.interceptor(e,n,r);if(u===!0||u===!1)return u;if(f(e,n,r))return!0;if(!("typeName"in e._def)||!("typeName"in n._def))throw new Error("Failed to compare type! "+e._def+" "+n._def);if(n instanceof t.z.ZodOptional||n instanceof t.z.ZodNullable)return c(e,n.unwrap(),r);if(e instanceof t.z.ZodUnion&&n instanceof t.z.ZodUnion){for(let o=0;o<e.options.length;o++)if(!n.options.some(i=>c(i,n.options[o],r)))return!1;return!0}if(e instanceof t.z.ZodUnion)return e.options.every(o=>c(o,n,r));if(n instanceof t.z.ZodUnion)return n.options.some(o=>c(e,o,r));if(e.constructor!==n.constructor)return!1;if(e instanceof t.z.ZodObject&&n instanceof t.z.ZodObject){const o=e.shape,s=n.shape;if(Object.keys(o).length<Object.keys(s).length)return!1;for(const i in s)if(!(i in o)||!c(o[i],s[i],r))return!1;return!0}if(e instanceof t.z.ZodArray&&n instanceof t.z.ZodArray)return c(e.element,n.element,r);if(e instanceof t.z.ZodTuple&&n instanceof t.z.ZodTuple){if(e.items.length<n.items.length)return!1;for(let o=0;o<n.items.length;o++)if(!c(e.items[o],n.items[o],r))return!1;return n._def.rest?e._def.rest?c(e._def.rest,n._def.rest,r):!1:!0}throw new Error("Failed to compare types!"+e._def.typeName+" "+n._def.typeName)};exports.isCompatibleType=c;exports.isSameType=f;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/utils.ts","../src/is-same-type.ts","../src/is-compatible-type.ts"],"sourcesContent":["import { z, type ZodType } from \"zod\";\n\nexport interface IsSameTypeOptions {\n deep: true;\n /**\n * Ignore all specific validations like min, max, length, email etc.\n *\n * You still can use `interceptor` to validate the type manually.\n */\n ignoreValidations: true;\n ignoreOptional: boolean;\n ignoreNullable: boolean;\n ignoreReadOnly: false;\n ignoreBranded: boolean;\n ignoreIntersectionsOrder: false;\n /**\n * A function that provides custom logic for comparing two ZodType instances.\n *\n * If the function returns `true` or `false`, the result will be used as the comparison result.\n * Otherwise, the default comparison logic will be used.\n *\n */\n interceptor: (\n a: ZodType,\n b: ZodType,\n options: IsSameTypeOptions,\n ) => boolean | void;\n}\n\nexport interface IsCompatibleTypeOptions extends IsSameTypeOptions {\n ignoreOptional: false;\n ignoreNullable: false;\n}\n\nexport const DEFAULT_COMPARE_TYPE_OPTIONS = {\n deep: true,\n ignoreOptional: false,\n ignoreNullable: false,\n ignoreReadOnly: false,\n ignoreBranded: false,\n ignoreValidations: true,\n ignoreIntersectionsOrder: false,\n interceptor: () => {},\n} as const satisfies IsSameTypeOptions;\n\n// See ZodFirstPartyTypeKind\nexport const isPrimitiveType = (a: ZodType): boolean => {\n return (\n a instanceof z.ZodString ||\n a instanceof z.ZodNumber ||\n a instanceof z.ZodNaN ||\n a instanceof z.ZodBigInt ||\n a instanceof z.ZodBoolean ||\n a instanceof z.ZodDate ||\n a instanceof z.ZodSymbol ||\n a instanceof z.ZodUndefined ||\n a instanceof z.ZodNull ||\n a instanceof z.ZodAny ||\n a instanceof z.ZodUnknown ||\n a instanceof z.ZodNever ||\n a instanceof z.ZodVoid\n );\n};\n","import type { EnumLike, ZodType } from \"zod\";\nimport { z } from \"zod\";\nimport {\n type IsSameTypeOptions,\n DEFAULT_COMPARE_TYPE_OPTIONS,\n isPrimitiveType,\n} from \"./utils.ts\";\n\n/**\n * isSameType is a function that checks if two ZodTypes are the same.\n *\n * Caveats:\n * - The function does not validate specific criteria such as min or max values, length, email, etc.\n * - It excludes comparisons involving methods like .describe(), .catch(), .default(), .refine(), and .transform().\n * - When comparing definitions with .or and .and, they are assessed sequentially based on their order.\n *\n * @param a - The first ZodType to compare.\n * @param b - The second ZodType to compare.\n * @returns A boolean indicating whether the two types are the same.\n *\n * @throws Will throw an error if it encounters an unknown type.\n *\n * @example\n * ```ts\n * isSameType(z.string(), z.string()); // true\n * isSameType(z.string(), z.number()); // false\n * ```\n */\nexport const isSameType = (\n a: ZodType,\n b: ZodType,\n options?: Partial<IsSameTypeOptions>,\n): boolean => {\n const opts = { ...DEFAULT_COMPARE_TYPE_OPTIONS, ...options };\n const interceptorResult = opts.interceptor(a, b, opts);\n if (interceptorResult === true || interceptorResult === false) {\n return interceptorResult;\n }\n\n if (a === undefined || b === undefined) {\n throw new Error(\"Failed to compare type! \" + a + \" \" + b);\n }\n\n if (a === b) {\n return true;\n }\n\n // compare constructor\n if (a.constructor !== b.constructor) {\n // https://stackoverflow.com/questions/24959862/how-to-tell-if-two-javascript-instances-are-of-the-same-class-type\n return false;\n }\n // See https://github.com/colinhacks/zod/blob/master/src/types.ts\n if (!(\"typeName\" in a._def) || !(\"typeName\" in b._def)) {\n throw new Error(\"Failed to compare type! \" + a._def + \" \" + b._def);\n }\n if (a._def.typeName !== b._def.typeName) {\n return false;\n }\n\n // ZodBranded\n if (opts.ignoreBranded) {\n if (a instanceof z.ZodBranded) {\n a = a.unwrap();\n }\n if (b instanceof z.ZodBranded) {\n b = b.unwrap();\n }\n return isSameType(a, b, opts);\n } else {\n if (a instanceof z.ZodBranded || b instanceof z.ZodBranded) {\n // We can not distinguish different branded type\n // throw new Error(\"Can not distinguish different branded type!\");\n return false;\n }\n }\n\n // ZodPromise ZodOptional ZodNullable ZodBranded\n if (\"unwrap\" in a && typeof a.unwrap === \"function\") {\n if (!(\"unwrap\" in b && typeof b.unwrap === \"function\")) {\n return false;\n }\n return isSameType(a.unwrap(), b.unwrap(), opts);\n }\n\n if (!opts.ignoreOptional && a.isOptional() !== b.isOptional()) return false;\n if (!opts.ignoreNullable && a.isNullable() !== b.isNullable()) return false;\n\n if (isPrimitiveType(a)) {\n // Already assert a and b are the same constructor\n return true;\n }\n\n // ZodObject\n if (a instanceof z.ZodObject && b instanceof z.ZodObject) {\n const aShape = a.shape;\n const bShape = b.shape;\n if (Object.keys(aShape).length !== Object.keys(bShape).length) return false;\n for (const key in aShape) {\n if (!(key in bShape)) return false;\n if (!isSameType(aShape[key], bShape[key], opts)) return false;\n }\n return true;\n }\n\n // ZodArray\n if (a instanceof z.ZodArray && b instanceof z.ZodArray) {\n return isSameType(a.element, b.element, opts);\n }\n\n // ZodTuple\n if (a instanceof z.ZodTuple && b instanceof z.ZodTuple) {\n if (a.items.length !== b.items.length) return false;\n for (let i = 0; i < a.items.length; i++) {\n if (!isSameType(a.items[i], b.items[i], opts)) return false;\n }\n // Compare rest\n if (a._def.rest || b._def.rest) {\n // If one has rest, the other must have rest\n if (!a._def.rest || !b._def.rest) return false;\n return isSameType(a._def.rest, b._def.rest, opts);\n }\n return true;\n }\n\n // ZodLiteral\n if (a instanceof z.ZodLiteral && b instanceof z.ZodLiteral) {\n return a.value === b.value;\n }\n\n // ZodIntersection aka and\n if (a instanceof z.ZodIntersection && b instanceof z.ZodIntersection) {\n return (\n isSameType(a._def.left, b._def.left, opts) &&\n isSameType(a._def.right, b._def.right, opts)\n );\n }\n\n // ZodUnion aka or\n if (a instanceof z.ZodUnion && b instanceof z.ZodUnion) {\n if (a.options.length !== b.options.length) return false;\n for (let i = 0; i < a.options.length; i++) {\n if (!isSameType(a.options[i], b.options[i], opts)) return false;\n }\n return true;\n }\n\n // ZodReadonly\n if (a instanceof z.ZodReadonly && b instanceof z.ZodReadonly) {\n return isSameType(a._def.innerType, b._def.innerType, opts);\n }\n\n // ZodRecord / ZodMap\n if (\n (a instanceof z.ZodRecord && b instanceof z.ZodRecord) ||\n (a instanceof z.ZodMap && b instanceof z.ZodMap)\n ) {\n return (\n isSameType(a.keySchema, b.keySchema, opts) &&\n isSameType(a.valueSchema, b.valueSchema, opts)\n );\n }\n\n // ZodSet\n if (a instanceof z.ZodSet && b instanceof z.ZodSet) {\n return isSameType(a._def.valueType, b._def.valueType, opts);\n }\n\n // ZodFunction\n if (a instanceof z.ZodFunction && b instanceof z.ZodFunction) {\n return (\n isSameType(a.parameters(), b.parameters(), opts) &&\n isSameType(a.returnType(), b.returnType(), opts)\n );\n }\n\n // ZodEnum\n if (a instanceof z.ZodEnum && b instanceof z.ZodEnum) {\n const optionsA: [string, ...string[]] = a.options;\n const optionsB: [string, ...string[]] = b.options;\n if (optionsA.length !== optionsB.length) return false;\n for (let i = 0; i < optionsA.length; i++) {\n if (optionsA[i] !== optionsB[i]) return false;\n }\n return true;\n }\n\n // ZodNativeEnum\n if (a instanceof z.ZodNativeEnum && b instanceof z.ZodNativeEnum) {\n const enumA: EnumLike = a.enum;\n const enumB: EnumLike = b.enum;\n if (Object.keys(enumA).length !== Object.keys(enumB).length) return false;\n for (const key in enumA) {\n if (enumA[key] !== enumB[key]) return false;\n }\n return true;\n }\n\n // ZodLazy\n // ZodEffects\n // ZodDefault\n // ZodCatch\n // ZodPipeline\n // ZodTransformer\n // ZodError\n console.error('Failed to compare type! \"' + a, b);\n throw new Error(\"Unknown type! \" + a._def.typeName + \" \" + b._def.typeName);\n};\n","import { z, type ZodType } from \"zod\";\nimport { isSameType } from \"./is-same-type.ts\";\nimport {\n DEFAULT_COMPARE_TYPE_OPTIONS,\n type IsCompatibleTypeOptions,\n} from \"./utils.ts\";\n\n/**\n * Check if a the higherType matches the lowerType\n *\n * @deprecated This a unstable API and still in development\n *\n * @param higherType The looser type\n * @param lowerType The stricter type\n *\n * @example\n * ```ts\n * isCompatibleType(z.string(), z.string()); // true\n *\n * isCompatibleType(\n * z.object({ name: z.string(), other: z.number() }),\n * z.object({ name: z.string() })\n * );\n * // true\n * ```\n */\nexport const isCompatibleType = (\n higherType: ZodType,\n lowerType: ZodType,\n options?: Partial<IsCompatibleTypeOptions>,\n): boolean => {\n const opts: IsCompatibleTypeOptions = {\n ...DEFAULT_COMPARE_TYPE_OPTIONS,\n ...options,\n };\n const interceptorResult = opts.interceptor(higherType, lowerType, opts);\n if (interceptorResult === true || interceptorResult === false) {\n return interceptorResult;\n }\n if (isSameType(higherType, lowerType, opts)) {\n return true;\n }\n\n if (!(\"typeName\" in higherType._def) || !(\"typeName\" in lowerType._def)) {\n throw new Error(\n \"Failed to compare type! \" + higherType._def + \" \" + lowerType._def,\n );\n }\n\n if (\n lowerType instanceof z.ZodOptional ||\n lowerType instanceof z.ZodNullable\n ) {\n return isCompatibleType(higherType, lowerType.unwrap(), opts);\n }\n\n // ZodUnion aka or\n if (higherType instanceof z.ZodUnion && lowerType instanceof z.ZodUnion) {\n for (let i = 0; i < higherType.options.length; i++) {\n const match = lowerType.options.some((option: ZodType) =>\n isCompatibleType(option, lowerType.options[i], opts),\n );\n if (!match) return false;\n }\n return true;\n }\n if (higherType instanceof z.ZodUnion) {\n return higherType.options.every((option: ZodType) =>\n isCompatibleType(option, lowerType, opts),\n );\n }\n if (lowerType instanceof z.ZodUnion) {\n return lowerType.options.some((option: ZodType) =>\n isCompatibleType(higherType, option, opts),\n );\n }\n\n // compare constructor\n if (higherType.constructor !== lowerType.constructor) {\n return false;\n }\n\n // ZodObject\n if (higherType instanceof z.ZodObject && lowerType instanceof z.ZodObject) {\n const superTypeShape = higherType.shape;\n const subTypeShape = lowerType.shape;\n if (Object.keys(superTypeShape).length < Object.keys(subTypeShape).length)\n return false;\n for (const key in subTypeShape) {\n if (!(key in superTypeShape)) return false;\n if (!isCompatibleType(superTypeShape[key], subTypeShape[key], opts)) {\n return false;\n }\n }\n return true;\n }\n\n // ZodArray\n if (higherType instanceof z.ZodArray && lowerType instanceof z.ZodArray) {\n return isCompatibleType(higherType.element, lowerType.element, opts);\n }\n\n // ZodTuple\n if (higherType instanceof z.ZodTuple && lowerType instanceof z.ZodTuple) {\n if (higherType.items.length < lowerType.items.length) return false;\n for (let i = 0; i < lowerType.items.length; i++) {\n if (!isCompatibleType(higherType.items[i], lowerType.items[i], opts)) {\n return false;\n }\n }\n // Check rest\n if (lowerType._def.rest) {\n if (!higherType._def.rest) return false;\n return isCompatibleType(higherType._def.rest, lowerType._def.rest, opts);\n }\n return true;\n }\n\n throw new Error(\n \"Failed to compare types!\" +\n higherType._def.typeName +\n \" \" +\n lowerType._def.typeName,\n );\n};\n"],"names":["DEFAULT_COMPARE_TYPE_OPTIONS","isPrimitiveType","a","z","isSameType","b","options","opts","interceptorResult","aShape","bShape","key","i","optionsA","optionsB","enumA","enumB","isCompatibleType","higherType","lowerType","option","superTypeShape","subTypeShape"],"mappings":"uGAkCaA,EAA+B,CAC1C,KAAM,GACN,eAAgB,GAChB,eAAgB,GAChB,eAAgB,GAChB,cAAe,GACf,kBAAmB,GACnB,yBAA0B,GAC1B,YAAa,IAAM,CAAC,CACtB,EAGaC,EAAmBC,GAE5BA,aAAaC,EAAE,EAAA,WACfD,aAAaC,EAAAA,EAAE,WACfD,aAAaC,IAAE,QACfD,aAAaC,IAAE,WACfD,aAAaC,EAAAA,EAAE,YACfD,aAAaC,IAAE,SACfD,aAAaC,EAAA,EAAE,WACfD,aAAaC,EAAAA,EAAE,cACfD,aAAaC,IAAE,SACfD,aAAaC,EAAE,EAAA,QACfD,aAAaC,EAAAA,EAAE,YACfD,aAAaC,EAAE,EAAA,UACfD,aAAaC,EAAE,EAAA,QChCNC,EAAa,CACxBF,EACAG,EACAC,IACY,CACZ,MAAMC,EAAO,CAAE,GAAGP,EAA8B,GAAGM,CAAQ,EACrDE,EAAoBD,EAAK,YAAYL,EAAGG,EAAGE,CAAI,EACjD,GAAAC,IAAsB,IAAQA,IAAsB,GAC/C,OAAAA,EAGL,GAAAN,IAAM,QAAaG,IAAM,OAC3B,MAAM,IAAI,MAAM,2BAA6BH,EAAI,IAAMG,CAAC,EAG1D,GAAIH,IAAMG,EACD,MAAA,GAIL,GAAAH,EAAE,cAAgBG,EAAE,YAEf,MAAA,GAGT,GAAI,EAAE,aAAcH,EAAE,OAAS,EAAE,aAAcG,EAAE,MAC/C,MAAM,IAAI,MAAM,2BAA6BH,EAAE,KAAO,IAAMG,EAAE,IAAI,EAEpE,GAAIH,EAAE,KAAK,WAAaG,EAAE,KAAK,SACtB,MAAA,GAIT,GAAIE,EAAK,cACH,OAAAL,aAAaC,IAAE,aACjBD,EAAIA,EAAE,UAEJG,aAAaF,IAAE,aACjBE,EAAIA,EAAE,UAEDD,EAAWF,EAAGG,EAAGE,CAAI,EAE5B,GAAIL,aAAaC,EAAAA,EAAE,YAAcE,aAAaF,EAAAA,EAAE,WAGvC,MAAA,GAKX,GAAI,WAAYD,GAAK,OAAOA,EAAE,QAAW,WACvC,MAAM,WAAYG,GAAK,OAAOA,EAAE,QAAW,WAGpCD,EAAWF,EAAE,OAAA,EAAUG,EAAE,OAAA,EAAUE,CAAI,EAFrC,GAMX,GADI,CAACA,EAAK,gBAAkBL,EAAE,WAAW,IAAMG,EAAE,WAAW,GACxD,CAACE,EAAK,gBAAkBL,EAAE,WAAW,IAAMG,EAAE,WAAW,EAAU,MAAA,GAElE,GAAAJ,EAAgBC,CAAC,EAEZ,MAAA,GAIT,GAAIA,aAAaC,EAAAA,EAAE,WAAaE,aAAaF,EAAAA,EAAE,UAAW,CACxD,MAAMM,EAASP,EAAE,MACXQ,EAASL,EAAE,MACb,GAAA,OAAO,KAAKI,CAAM,EAAE,SAAW,OAAO,KAAKC,CAAM,EAAE,OAAe,MAAA,GACtE,UAAWC,KAAOF,EAEZ,GADA,EAAEE,KAAOD,IACT,CAACN,EAAWK,EAAOE,CAAG,EAAGD,EAAOC,CAAG,EAAGJ,CAAI,EAAU,MAAA,GAEnD,MAAA,EACT,CAGA,GAAIL,aAAaC,EAAAA,EAAE,UAAYE,aAAaF,EAAAA,EAAE,SAC5C,OAAOC,EAAWF,EAAE,QAASG,EAAE,QAASE,CAAI,EAI9C,GAAIL,aAAaC,EAAAA,EAAE,UAAYE,aAAaF,EAAAA,EAAE,SAAU,CACtD,GAAID,EAAE,MAAM,SAAWG,EAAE,MAAM,OAAe,MAAA,GAC9C,QAASO,EAAI,EAAGA,EAAIV,EAAE,MAAM,OAAQU,IAC9B,GAAA,CAACR,EAAWF,EAAE,MAAMU,CAAC,EAAGP,EAAE,MAAMO,CAAC,EAAGL,CAAI,EAAU,MAAA,GAGxD,OAAIL,EAAE,KAAK,MAAQG,EAAE,KAAK,KAEpB,CAACH,EAAE,KAAK,MAAQ,CAACG,EAAE,KAAK,KAAa,GAClCD,EAAWF,EAAE,KAAK,KAAMG,EAAE,KAAK,KAAME,CAAI,EAE3C,EACT,CAGA,GAAIL,aAAaC,EAAAA,EAAE,YAAcE,aAAaF,EAAAA,EAAE,WACvC,OAAAD,EAAE,QAAUG,EAAE,MAIvB,GAAIH,aAAaC,EAAAA,EAAE,iBAAmBE,aAAaF,EAAAA,EAAE,gBACnD,OACEC,EAAWF,EAAE,KAAK,KAAMG,EAAE,KAAK,KAAME,CAAI,GACzCH,EAAWF,EAAE,KAAK,MAAOG,EAAE,KAAK,MAAOE,CAAI,EAK/C,GAAIL,aAAaC,EAAAA,EAAE,UAAYE,aAAaF,EAAAA,EAAE,SAAU,CACtD,GAAID,EAAE,QAAQ,SAAWG,EAAE,QAAQ,OAAe,MAAA,GAClD,QAASO,EAAI,EAAGA,EAAIV,EAAE,QAAQ,OAAQU,IAChC,GAAA,CAACR,EAAWF,EAAE,QAAQU,CAAC,EAAGP,EAAE,QAAQO,CAAC,EAAGL,CAAI,EAAU,MAAA,GAErD,MAAA,EACT,CAGA,GAAIL,aAAaC,EAAAA,EAAE,aAAeE,aAAaF,EAAAA,EAAE,YAC/C,OAAOC,EAAWF,EAAE,KAAK,UAAWG,EAAE,KAAK,UAAWE,CAAI,EAKzD,GAAAL,aAAaC,EAAE,EAAA,WAAaE,aAAaF,EAAAA,EAAE,WAC3CD,aAAaC,EAAE,EAAA,QAAUE,aAAaF,EAAAA,EAAE,OAEzC,OACEC,EAAWF,EAAE,UAAWG,EAAE,UAAWE,CAAI,GACzCH,EAAWF,EAAE,YAAaG,EAAE,YAAaE,CAAI,EAKjD,GAAIL,aAAaC,EAAAA,EAAE,QAAUE,aAAaF,EAAAA,EAAE,OAC1C,OAAOC,EAAWF,EAAE,KAAK,UAAWG,EAAE,KAAK,UAAWE,CAAI,EAI5D,GAAIL,aAAaC,EAAAA,EAAE,aAAeE,aAAaF,EAAAA,EAAE,YAC/C,OACEC,EAAWF,EAAE,WAAc,EAAAG,EAAE,aAAcE,CAAI,GAC/CH,EAAWF,EAAE,WAAW,EAAGG,EAAE,WAAA,EAAcE,CAAI,EAKnD,GAAIL,aAAaC,EAAAA,EAAE,SAAWE,aAAaF,EAAAA,EAAE,QAAS,CACpD,MAAMU,EAAkCX,EAAE,QACpCY,EAAkCT,EAAE,QACtC,GAAAQ,EAAS,SAAWC,EAAS,OAAe,MAAA,GAChD,QAAS,EAAI,EAAG,EAAID,EAAS,OAAQ,IACnC,GAAIA,EAAS,CAAC,IAAMC,EAAS,CAAC,EAAU,MAAA,GAEnC,MAAA,EACT,CAGA,GAAIZ,aAAaC,EAAAA,EAAE,eAAiBE,aAAaF,EAAAA,EAAE,cAAe,CAChE,MAAMY,EAAkBb,EAAE,KACpBc,EAAkBX,EAAE,KACtB,GAAA,OAAO,KAAKU,CAAK,EAAE,SAAW,OAAO,KAAKC,CAAK,EAAE,OAAe,MAAA,GACpE,UAAWL,KAAOI,EAChB,GAAIA,EAAMJ,CAAG,IAAMK,EAAML,CAAG,EAAU,MAAA,GAEjC,MAAA,EACT,CASQ,cAAA,MAAM,4BAA8BT,EAAGG,CAAC,EAC1C,IAAI,MAAM,iBAAmBH,EAAE,KAAK,SAAW,IAAMG,EAAE,KAAK,QAAQ,CAC5E,ECrLaY,EAAmB,CAC9BC,EACAC,EACAb,IACY,CACZ,MAAMC,EAAgC,CACpC,GAAGP,EACH,GAAGM,CAAA,EAECE,EAAoBD,EAAK,YAAYW,EAAYC,EAAWZ,CAAI,EAClE,GAAAC,IAAsB,IAAQA,IAAsB,GAC/C,OAAAA,EAET,GAAIJ,EAAWc,EAAYC,EAAWZ,CAAI,EACjC,MAAA,GAGT,GAAI,EAAE,aAAcW,EAAW,OAAS,EAAE,aAAcC,EAAU,MAChE,MAAM,IAAI,MACR,2BAA6BD,EAAW,KAAO,IAAMC,EAAU,IAAA,EAInE,GACEA,aAAqBhB,EAAAA,EAAE,aACvBgB,aAAqBhB,EAAAA,EAAE,YAEvB,OAAOc,EAAiBC,EAAYC,EAAU,SAAUZ,CAAI,EAI9D,GAAIW,aAAsBf,EAAAA,EAAE,UAAYgB,aAAqBhB,EAAAA,EAAE,SAAU,CACvE,QAASS,EAAI,EAAGA,EAAIM,EAAW,QAAQ,OAAQN,IAI7C,GAAI,CAHUO,EAAU,QAAQ,KAAMC,GACpCH,EAAiBG,EAAQD,EAAU,QAAQP,CAAC,EAAGL,CAAI,CAAA,EAElC,MAAA,GAEd,MAAA,EACT,CACI,GAAAW,aAAsBf,IAAE,SAC1B,OAAOe,EAAW,QAAQ,MAAOE,GAC/BH,EAAiBG,EAAQD,EAAWZ,CAAI,CAAA,EAGxC,GAAAY,aAAqBhB,IAAE,SACzB,OAAOgB,EAAU,QAAQ,KAAMC,GAC7BH,EAAiBC,EAAYE,EAAQb,CAAI,CAAA,EAKzC,GAAAW,EAAW,cAAgBC,EAAU,YAChC,MAAA,GAIT,GAAID,aAAsBf,EAAAA,EAAE,WAAagB,aAAqBhB,EAAAA,EAAE,UAAW,CACzE,MAAMkB,EAAiBH,EAAW,MAC5BI,EAAeH,EAAU,MAC3B,GAAA,OAAO,KAAKE,CAAc,EAAE,OAAS,OAAO,KAAKC,CAAY,EAAE,OAC1D,MAAA,GACT,UAAWX,KAAOW,EAEZ,GADA,EAAEX,KAAOU,IACT,CAACJ,EAAiBI,EAAeV,CAAG,EAAGW,EAAaX,CAAG,EAAGJ,CAAI,EACzD,MAAA,GAGJ,MAAA,EACT,CAGA,GAAIW,aAAsBf,EAAAA,EAAE,UAAYgB,aAAqBhB,EAAAA,EAAE,SAC7D,OAAOc,EAAiBC,EAAW,QAASC,EAAU,QAASZ,CAAI,EAIrE,GAAIW,aAAsBf,EAAAA,EAAE,UAAYgB,aAAqBhB,EAAAA,EAAE,SAAU,CACvE,GAAIe,EAAW,MAAM,OAASC,EAAU,MAAM,OAAe,MAAA,GAC7D,QAASP,EAAI,EAAGA,EAAIO,EAAU,MAAM,OAAQP,IACtC,GAAA,CAACK,EAAiBC,EAAW,MAAMN,CAAC,EAAGO,EAAU,MAAMP,CAAC,EAAGL,CAAI,EAC1D,MAAA,GAIP,OAAAY,EAAU,KAAK,KACZD,EAAW,KAAK,KACdD,EAAiBC,EAAW,KAAK,KAAMC,EAAU,KAAK,KAAMZ,CAAI,EADrC,GAG7B,EACT,CAEA,MAAM,IAAI,MACR,2BACEW,EAAW,KAAK,SAChB,IACAC,EAAU,KAAK,QAAA,CAErB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ZodType } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check if a the higherType matches the lowerType
|
|
5
|
+
*
|
|
6
|
+
* @deprecated This a unstable API and still in development
|
|
7
|
+
*
|
|
8
|
+
* @param higherType The looser type
|
|
9
|
+
* @param lowerType The stricter type
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* isCompatibleType(z.string(), z.string()); // true
|
|
14
|
+
*
|
|
15
|
+
* isCompatibleType(
|
|
16
|
+
* z.object({ name: z.string(), other: z.number() }),
|
|
17
|
+
* z.object({ name: z.string() })
|
|
18
|
+
* );
|
|
19
|
+
* // true
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare const isCompatibleType: (higherType: ZodType, lowerType: ZodType, options?: Partial<IsCompatibleTypeOptions>) => boolean;
|
|
23
|
+
|
|
24
|
+
declare interface IsCompatibleTypeOptions extends IsSameTypeOptions {
|
|
25
|
+
ignoreOptional: false;
|
|
26
|
+
ignoreNullable: false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* isSameType is a function that checks if two ZodTypes are the same.
|
|
31
|
+
*
|
|
32
|
+
* Caveats:
|
|
33
|
+
* - The function does not validate specific criteria such as min or max values, length, email, etc.
|
|
34
|
+
* - It excludes comparisons involving methods like .describe(), .catch(), .default(), .refine(), and .transform().
|
|
35
|
+
* - When comparing definitions with .or and .and, they are assessed sequentially based on their order.
|
|
36
|
+
*
|
|
37
|
+
* @param a - The first ZodType to compare.
|
|
38
|
+
* @param b - The second ZodType to compare.
|
|
39
|
+
* @returns A boolean indicating whether the two types are the same.
|
|
40
|
+
*
|
|
41
|
+
* @throws Will throw an error if it encounters an unknown type.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* isSameType(z.string(), z.string()); // true
|
|
46
|
+
* isSameType(z.string(), z.number()); // false
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare const isSameType: (a: ZodType, b: ZodType, options?: Partial<IsSameTypeOptions>) => boolean;
|
|
50
|
+
|
|
51
|
+
export declare interface IsSameTypeOptions {
|
|
52
|
+
deep: true;
|
|
53
|
+
/**
|
|
54
|
+
* Ignore all specific validations like min, max, length, email etc.
|
|
55
|
+
*
|
|
56
|
+
* You still can use `interceptor` to validate the type manually.
|
|
57
|
+
*/
|
|
58
|
+
ignoreValidations: true;
|
|
59
|
+
ignoreOptional: boolean;
|
|
60
|
+
ignoreNullable: boolean;
|
|
61
|
+
ignoreReadOnly: false;
|
|
62
|
+
ignoreBranded: boolean;
|
|
63
|
+
ignoreIntersectionsOrder: false;
|
|
64
|
+
/**
|
|
65
|
+
* A function that provides custom logic for comparing two ZodType instances.
|
|
66
|
+
*
|
|
67
|
+
* If the function returns `true` or `false`, the result will be used as the comparison result.
|
|
68
|
+
* Otherwise, the default comparison logic will be used.
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
71
|
+
interceptor: (a: ZodType, b: ZodType, options: IsSameTypeOptions) => boolean | void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z as t } from "zod";
|
|
2
|
+
const a = {
|
|
3
|
+
deep: !0,
|
|
4
|
+
ignoreOptional: !1,
|
|
5
|
+
ignoreNullable: !1,
|
|
6
|
+
ignoreReadOnly: !1,
|
|
7
|
+
ignoreBranded: !1,
|
|
8
|
+
ignoreValidations: !0,
|
|
9
|
+
ignoreIntersectionsOrder: !1,
|
|
10
|
+
interceptor: () => {
|
|
11
|
+
}
|
|
12
|
+
}, l = (e) => e instanceof t.ZodString || e instanceof t.ZodNumber || e instanceof t.ZodNaN || e instanceof t.ZodBigInt || e instanceof t.ZodBoolean || e instanceof t.ZodDate || e instanceof t.ZodSymbol || e instanceof t.ZodUndefined || e instanceof t.ZodNull || e instanceof t.ZodAny || e instanceof t.ZodUnknown || e instanceof t.ZodNever || e instanceof t.ZodVoid, f = (e, n, d) => {
|
|
13
|
+
const r = { ...a, ...d }, u = r.interceptor(e, n, r);
|
|
14
|
+
if (u === !0 || u === !1)
|
|
15
|
+
return u;
|
|
16
|
+
if (e === void 0 || n === void 0)
|
|
17
|
+
throw new Error("Failed to compare type! " + e + " " + n);
|
|
18
|
+
if (e === n)
|
|
19
|
+
return !0;
|
|
20
|
+
if (e.constructor !== n.constructor)
|
|
21
|
+
return !1;
|
|
22
|
+
if (!("typeName" in e._def) || !("typeName" in n._def))
|
|
23
|
+
throw new Error("Failed to compare type! " + e._def + " " + n._def);
|
|
24
|
+
if (e._def.typeName !== n._def.typeName)
|
|
25
|
+
return !1;
|
|
26
|
+
if (r.ignoreBranded)
|
|
27
|
+
return e instanceof t.ZodBranded && (e = e.unwrap()), n instanceof t.ZodBranded && (n = n.unwrap()), f(e, n, r);
|
|
28
|
+
if (e instanceof t.ZodBranded || n instanceof t.ZodBranded)
|
|
29
|
+
return !1;
|
|
30
|
+
if ("unwrap" in e && typeof e.unwrap == "function")
|
|
31
|
+
return "unwrap" in n && typeof n.unwrap == "function" ? f(e.unwrap(), n.unwrap(), r) : !1;
|
|
32
|
+
if (!r.ignoreOptional && e.isOptional() !== n.isOptional() || !r.ignoreNullable && e.isNullable() !== n.isNullable())
|
|
33
|
+
return !1;
|
|
34
|
+
if (l(e))
|
|
35
|
+
return !0;
|
|
36
|
+
if (e instanceof t.ZodObject && n instanceof t.ZodObject) {
|
|
37
|
+
const o = e.shape, s = n.shape;
|
|
38
|
+
if (Object.keys(o).length !== Object.keys(s).length)
|
|
39
|
+
return !1;
|
|
40
|
+
for (const i in o)
|
|
41
|
+
if (!(i in s) || !f(o[i], s[i], r))
|
|
42
|
+
return !1;
|
|
43
|
+
return !0;
|
|
44
|
+
}
|
|
45
|
+
if (e instanceof t.ZodArray && n instanceof t.ZodArray)
|
|
46
|
+
return f(e.element, n.element, r);
|
|
47
|
+
if (e instanceof t.ZodTuple && n instanceof t.ZodTuple) {
|
|
48
|
+
if (e.items.length !== n.items.length)
|
|
49
|
+
return !1;
|
|
50
|
+
for (let o = 0; o < e.items.length; o++)
|
|
51
|
+
if (!f(e.items[o], n.items[o], r))
|
|
52
|
+
return !1;
|
|
53
|
+
return e._def.rest || n._def.rest ? !e._def.rest || !n._def.rest ? !1 : f(e._def.rest, n._def.rest, r) : !0;
|
|
54
|
+
}
|
|
55
|
+
if (e instanceof t.ZodLiteral && n instanceof t.ZodLiteral)
|
|
56
|
+
return e.value === n.value;
|
|
57
|
+
if (e instanceof t.ZodIntersection && n instanceof t.ZodIntersection)
|
|
58
|
+
return f(e._def.left, n._def.left, r) && f(e._def.right, n._def.right, r);
|
|
59
|
+
if (e instanceof t.ZodUnion && n instanceof t.ZodUnion) {
|
|
60
|
+
if (e.options.length !== n.options.length)
|
|
61
|
+
return !1;
|
|
62
|
+
for (let o = 0; o < e.options.length; o++)
|
|
63
|
+
if (!f(e.options[o], n.options[o], r))
|
|
64
|
+
return !1;
|
|
65
|
+
return !0;
|
|
66
|
+
}
|
|
67
|
+
if (e instanceof t.ZodReadonly && n instanceof t.ZodReadonly)
|
|
68
|
+
return f(e._def.innerType, n._def.innerType, r);
|
|
69
|
+
if (e instanceof t.ZodRecord && n instanceof t.ZodRecord || e instanceof t.ZodMap && n instanceof t.ZodMap)
|
|
70
|
+
return f(e.keySchema, n.keySchema, r) && f(e.valueSchema, n.valueSchema, r);
|
|
71
|
+
if (e instanceof t.ZodSet && n instanceof t.ZodSet)
|
|
72
|
+
return f(e._def.valueType, n._def.valueType, r);
|
|
73
|
+
if (e instanceof t.ZodFunction && n instanceof t.ZodFunction)
|
|
74
|
+
return f(e.parameters(), n.parameters(), r) && f(e.returnType(), n.returnType(), r);
|
|
75
|
+
if (e instanceof t.ZodEnum && n instanceof t.ZodEnum) {
|
|
76
|
+
const o = e.options, s = n.options;
|
|
77
|
+
if (o.length !== s.length)
|
|
78
|
+
return !1;
|
|
79
|
+
for (let i = 0; i < o.length; i++)
|
|
80
|
+
if (o[i] !== s[i])
|
|
81
|
+
return !1;
|
|
82
|
+
return !0;
|
|
83
|
+
}
|
|
84
|
+
if (e instanceof t.ZodNativeEnum && n instanceof t.ZodNativeEnum) {
|
|
85
|
+
const o = e.enum, s = n.enum;
|
|
86
|
+
if (Object.keys(o).length !== Object.keys(s).length)
|
|
87
|
+
return !1;
|
|
88
|
+
for (const i in o)
|
|
89
|
+
if (o[i] !== s[i])
|
|
90
|
+
return !1;
|
|
91
|
+
return !0;
|
|
92
|
+
}
|
|
93
|
+
throw console.error('Failed to compare type! "' + e, n), new Error("Unknown type! " + e._def.typeName + " " + n._def.typeName);
|
|
94
|
+
}, c = (e, n, d) => {
|
|
95
|
+
const r = {
|
|
96
|
+
...a,
|
|
97
|
+
...d
|
|
98
|
+
}, u = r.interceptor(e, n, r);
|
|
99
|
+
if (u === !0 || u === !1)
|
|
100
|
+
return u;
|
|
101
|
+
if (f(e, n, r))
|
|
102
|
+
return !0;
|
|
103
|
+
if (!("typeName" in e._def) || !("typeName" in n._def))
|
|
104
|
+
throw new Error(
|
|
105
|
+
"Failed to compare type! " + e._def + " " + n._def
|
|
106
|
+
);
|
|
107
|
+
if (n instanceof t.ZodOptional || n instanceof t.ZodNullable)
|
|
108
|
+
return c(e, n.unwrap(), r);
|
|
109
|
+
if (e instanceof t.ZodUnion && n instanceof t.ZodUnion) {
|
|
110
|
+
for (let o = 0; o < e.options.length; o++)
|
|
111
|
+
if (!n.options.some(
|
|
112
|
+
(i) => c(i, n.options[o], r)
|
|
113
|
+
))
|
|
114
|
+
return !1;
|
|
115
|
+
return !0;
|
|
116
|
+
}
|
|
117
|
+
if (e instanceof t.ZodUnion)
|
|
118
|
+
return e.options.every(
|
|
119
|
+
(o) => c(o, n, r)
|
|
120
|
+
);
|
|
121
|
+
if (n instanceof t.ZodUnion)
|
|
122
|
+
return n.options.some(
|
|
123
|
+
(o) => c(e, o, r)
|
|
124
|
+
);
|
|
125
|
+
if (e.constructor !== n.constructor)
|
|
126
|
+
return !1;
|
|
127
|
+
if (e instanceof t.ZodObject && n instanceof t.ZodObject) {
|
|
128
|
+
const o = e.shape, s = n.shape;
|
|
129
|
+
if (Object.keys(o).length < Object.keys(s).length)
|
|
130
|
+
return !1;
|
|
131
|
+
for (const i in s)
|
|
132
|
+
if (!(i in o) || !c(o[i], s[i], r))
|
|
133
|
+
return !1;
|
|
134
|
+
return !0;
|
|
135
|
+
}
|
|
136
|
+
if (e instanceof t.ZodArray && n instanceof t.ZodArray)
|
|
137
|
+
return c(e.element, n.element, r);
|
|
138
|
+
if (e instanceof t.ZodTuple && n instanceof t.ZodTuple) {
|
|
139
|
+
if (e.items.length < n.items.length)
|
|
140
|
+
return !1;
|
|
141
|
+
for (let o = 0; o < n.items.length; o++)
|
|
142
|
+
if (!c(e.items[o], n.items[o], r))
|
|
143
|
+
return !1;
|
|
144
|
+
return n._def.rest ? e._def.rest ? c(e._def.rest, n._def.rest, r) : !1 : !0;
|
|
145
|
+
}
|
|
146
|
+
throw new Error(
|
|
147
|
+
"Failed to compare types!" + e._def.typeName + " " + n._def.typeName
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
export {
|
|
151
|
+
c as isCompatibleType,
|
|
152
|
+
f as isSameType
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/utils.ts","../src/is-same-type.ts","../src/is-compatible-type.ts"],"sourcesContent":["import { z, type ZodType } from \"zod\";\n\nexport interface IsSameTypeOptions {\n deep: true;\n /**\n * Ignore all specific validations like min, max, length, email etc.\n *\n * You still can use `interceptor` to validate the type manually.\n */\n ignoreValidations: true;\n ignoreOptional: boolean;\n ignoreNullable: boolean;\n ignoreReadOnly: false;\n ignoreBranded: boolean;\n ignoreIntersectionsOrder: false;\n /**\n * A function that provides custom logic for comparing two ZodType instances.\n *\n * If the function returns `true` or `false`, the result will be used as the comparison result.\n * Otherwise, the default comparison logic will be used.\n *\n */\n interceptor: (\n a: ZodType,\n b: ZodType,\n options: IsSameTypeOptions,\n ) => boolean | void;\n}\n\nexport interface IsCompatibleTypeOptions extends IsSameTypeOptions {\n ignoreOptional: false;\n ignoreNullable: false;\n}\n\nexport const DEFAULT_COMPARE_TYPE_OPTIONS = {\n deep: true,\n ignoreOptional: false,\n ignoreNullable: false,\n ignoreReadOnly: false,\n ignoreBranded: false,\n ignoreValidations: true,\n ignoreIntersectionsOrder: false,\n interceptor: () => {},\n} as const satisfies IsSameTypeOptions;\n\n// See ZodFirstPartyTypeKind\nexport const isPrimitiveType = (a: ZodType): boolean => {\n return (\n a instanceof z.ZodString ||\n a instanceof z.ZodNumber ||\n a instanceof z.ZodNaN ||\n a instanceof z.ZodBigInt ||\n a instanceof z.ZodBoolean ||\n a instanceof z.ZodDate ||\n a instanceof z.ZodSymbol ||\n a instanceof z.ZodUndefined ||\n a instanceof z.ZodNull ||\n a instanceof z.ZodAny ||\n a instanceof z.ZodUnknown ||\n a instanceof z.ZodNever ||\n a instanceof z.ZodVoid\n );\n};\n","import type { EnumLike, ZodType } from \"zod\";\nimport { z } from \"zod\";\nimport {\n type IsSameTypeOptions,\n DEFAULT_COMPARE_TYPE_OPTIONS,\n isPrimitiveType,\n} from \"./utils.ts\";\n\n/**\n * isSameType is a function that checks if two ZodTypes are the same.\n *\n * Caveats:\n * - The function does not validate specific criteria such as min or max values, length, email, etc.\n * - It excludes comparisons involving methods like .describe(), .catch(), .default(), .refine(), and .transform().\n * - When comparing definitions with .or and .and, they are assessed sequentially based on their order.\n *\n * @param a - The first ZodType to compare.\n * @param b - The second ZodType to compare.\n * @returns A boolean indicating whether the two types are the same.\n *\n * @throws Will throw an error if it encounters an unknown type.\n *\n * @example\n * ```ts\n * isSameType(z.string(), z.string()); // true\n * isSameType(z.string(), z.number()); // false\n * ```\n */\nexport const isSameType = (\n a: ZodType,\n b: ZodType,\n options?: Partial<IsSameTypeOptions>,\n): boolean => {\n const opts = { ...DEFAULT_COMPARE_TYPE_OPTIONS, ...options };\n const interceptorResult = opts.interceptor(a, b, opts);\n if (interceptorResult === true || interceptorResult === false) {\n return interceptorResult;\n }\n\n if (a === undefined || b === undefined) {\n throw new Error(\"Failed to compare type! \" + a + \" \" + b);\n }\n\n if (a === b) {\n return true;\n }\n\n // compare constructor\n if (a.constructor !== b.constructor) {\n // https://stackoverflow.com/questions/24959862/how-to-tell-if-two-javascript-instances-are-of-the-same-class-type\n return false;\n }\n // See https://github.com/colinhacks/zod/blob/master/src/types.ts\n if (!(\"typeName\" in a._def) || !(\"typeName\" in b._def)) {\n throw new Error(\"Failed to compare type! \" + a._def + \" \" + b._def);\n }\n if (a._def.typeName !== b._def.typeName) {\n return false;\n }\n\n // ZodBranded\n if (opts.ignoreBranded) {\n if (a instanceof z.ZodBranded) {\n a = a.unwrap();\n }\n if (b instanceof z.ZodBranded) {\n b = b.unwrap();\n }\n return isSameType(a, b, opts);\n } else {\n if (a instanceof z.ZodBranded || b instanceof z.ZodBranded) {\n // We can not distinguish different branded type\n // throw new Error(\"Can not distinguish different branded type!\");\n return false;\n }\n }\n\n // ZodPromise ZodOptional ZodNullable ZodBranded\n if (\"unwrap\" in a && typeof a.unwrap === \"function\") {\n if (!(\"unwrap\" in b && typeof b.unwrap === \"function\")) {\n return false;\n }\n return isSameType(a.unwrap(), b.unwrap(), opts);\n }\n\n if (!opts.ignoreOptional && a.isOptional() !== b.isOptional()) return false;\n if (!opts.ignoreNullable && a.isNullable() !== b.isNullable()) return false;\n\n if (isPrimitiveType(a)) {\n // Already assert a and b are the same constructor\n return true;\n }\n\n // ZodObject\n if (a instanceof z.ZodObject && b instanceof z.ZodObject) {\n const aShape = a.shape;\n const bShape = b.shape;\n if (Object.keys(aShape).length !== Object.keys(bShape).length) return false;\n for (const key in aShape) {\n if (!(key in bShape)) return false;\n if (!isSameType(aShape[key], bShape[key], opts)) return false;\n }\n return true;\n }\n\n // ZodArray\n if (a instanceof z.ZodArray && b instanceof z.ZodArray) {\n return isSameType(a.element, b.element, opts);\n }\n\n // ZodTuple\n if (a instanceof z.ZodTuple && b instanceof z.ZodTuple) {\n if (a.items.length !== b.items.length) return false;\n for (let i = 0; i < a.items.length; i++) {\n if (!isSameType(a.items[i], b.items[i], opts)) return false;\n }\n // Compare rest\n if (a._def.rest || b._def.rest) {\n // If one has rest, the other must have rest\n if (!a._def.rest || !b._def.rest) return false;\n return isSameType(a._def.rest, b._def.rest, opts);\n }\n return true;\n }\n\n // ZodLiteral\n if (a instanceof z.ZodLiteral && b instanceof z.ZodLiteral) {\n return a.value === b.value;\n }\n\n // ZodIntersection aka and\n if (a instanceof z.ZodIntersection && b instanceof z.ZodIntersection) {\n return (\n isSameType(a._def.left, b._def.left, opts) &&\n isSameType(a._def.right, b._def.right, opts)\n );\n }\n\n // ZodUnion aka or\n if (a instanceof z.ZodUnion && b instanceof z.ZodUnion) {\n if (a.options.length !== b.options.length) return false;\n for (let i = 0; i < a.options.length; i++) {\n if (!isSameType(a.options[i], b.options[i], opts)) return false;\n }\n return true;\n }\n\n // ZodReadonly\n if (a instanceof z.ZodReadonly && b instanceof z.ZodReadonly) {\n return isSameType(a._def.innerType, b._def.innerType, opts);\n }\n\n // ZodRecord / ZodMap\n if (\n (a instanceof z.ZodRecord && b instanceof z.ZodRecord) ||\n (a instanceof z.ZodMap && b instanceof z.ZodMap)\n ) {\n return (\n isSameType(a.keySchema, b.keySchema, opts) &&\n isSameType(a.valueSchema, b.valueSchema, opts)\n );\n }\n\n // ZodSet\n if (a instanceof z.ZodSet && b instanceof z.ZodSet) {\n return isSameType(a._def.valueType, b._def.valueType, opts);\n }\n\n // ZodFunction\n if (a instanceof z.ZodFunction && b instanceof z.ZodFunction) {\n return (\n isSameType(a.parameters(), b.parameters(), opts) &&\n isSameType(a.returnType(), b.returnType(), opts)\n );\n }\n\n // ZodEnum\n if (a instanceof z.ZodEnum && b instanceof z.ZodEnum) {\n const optionsA: [string, ...string[]] = a.options;\n const optionsB: [string, ...string[]] = b.options;\n if (optionsA.length !== optionsB.length) return false;\n for (let i = 0; i < optionsA.length; i++) {\n if (optionsA[i] !== optionsB[i]) return false;\n }\n return true;\n }\n\n // ZodNativeEnum\n if (a instanceof z.ZodNativeEnum && b instanceof z.ZodNativeEnum) {\n const enumA: EnumLike = a.enum;\n const enumB: EnumLike = b.enum;\n if (Object.keys(enumA).length !== Object.keys(enumB).length) return false;\n for (const key in enumA) {\n if (enumA[key] !== enumB[key]) return false;\n }\n return true;\n }\n\n // ZodLazy\n // ZodEffects\n // ZodDefault\n // ZodCatch\n // ZodPipeline\n // ZodTransformer\n // ZodError\n console.error('Failed to compare type! \"' + a, b);\n throw new Error(\"Unknown type! \" + a._def.typeName + \" \" + b._def.typeName);\n};\n","import { z, type ZodType } from \"zod\";\nimport { isSameType } from \"./is-same-type.ts\";\nimport {\n DEFAULT_COMPARE_TYPE_OPTIONS,\n type IsCompatibleTypeOptions,\n} from \"./utils.ts\";\n\n/**\n * Check if a the higherType matches the lowerType\n *\n * @deprecated This a unstable API and still in development\n *\n * @param higherType The looser type\n * @param lowerType The stricter type\n *\n * @example\n * ```ts\n * isCompatibleType(z.string(), z.string()); // true\n *\n * isCompatibleType(\n * z.object({ name: z.string(), other: z.number() }),\n * z.object({ name: z.string() })\n * );\n * // true\n * ```\n */\nexport const isCompatibleType = (\n higherType: ZodType,\n lowerType: ZodType,\n options?: Partial<IsCompatibleTypeOptions>,\n): boolean => {\n const opts: IsCompatibleTypeOptions = {\n ...DEFAULT_COMPARE_TYPE_OPTIONS,\n ...options,\n };\n const interceptorResult = opts.interceptor(higherType, lowerType, opts);\n if (interceptorResult === true || interceptorResult === false) {\n return interceptorResult;\n }\n if (isSameType(higherType, lowerType, opts)) {\n return true;\n }\n\n if (!(\"typeName\" in higherType._def) || !(\"typeName\" in lowerType._def)) {\n throw new Error(\n \"Failed to compare type! \" + higherType._def + \" \" + lowerType._def,\n );\n }\n\n if (\n lowerType instanceof z.ZodOptional ||\n lowerType instanceof z.ZodNullable\n ) {\n return isCompatibleType(higherType, lowerType.unwrap(), opts);\n }\n\n // ZodUnion aka or\n if (higherType instanceof z.ZodUnion && lowerType instanceof z.ZodUnion) {\n for (let i = 0; i < higherType.options.length; i++) {\n const match = lowerType.options.some((option: ZodType) =>\n isCompatibleType(option, lowerType.options[i], opts),\n );\n if (!match) return false;\n }\n return true;\n }\n if (higherType instanceof z.ZodUnion) {\n return higherType.options.every((option: ZodType) =>\n isCompatibleType(option, lowerType, opts),\n );\n }\n if (lowerType instanceof z.ZodUnion) {\n return lowerType.options.some((option: ZodType) =>\n isCompatibleType(higherType, option, opts),\n );\n }\n\n // compare constructor\n if (higherType.constructor !== lowerType.constructor) {\n return false;\n }\n\n // ZodObject\n if (higherType instanceof z.ZodObject && lowerType instanceof z.ZodObject) {\n const superTypeShape = higherType.shape;\n const subTypeShape = lowerType.shape;\n if (Object.keys(superTypeShape).length < Object.keys(subTypeShape).length)\n return false;\n for (const key in subTypeShape) {\n if (!(key in superTypeShape)) return false;\n if (!isCompatibleType(superTypeShape[key], subTypeShape[key], opts)) {\n return false;\n }\n }\n return true;\n }\n\n // ZodArray\n if (higherType instanceof z.ZodArray && lowerType instanceof z.ZodArray) {\n return isCompatibleType(higherType.element, lowerType.element, opts);\n }\n\n // ZodTuple\n if (higherType instanceof z.ZodTuple && lowerType instanceof z.ZodTuple) {\n if (higherType.items.length < lowerType.items.length) return false;\n for (let i = 0; i < lowerType.items.length; i++) {\n if (!isCompatibleType(higherType.items[i], lowerType.items[i], opts)) {\n return false;\n }\n }\n // Check rest\n if (lowerType._def.rest) {\n if (!higherType._def.rest) return false;\n return isCompatibleType(higherType._def.rest, lowerType._def.rest, opts);\n }\n return true;\n }\n\n throw new Error(\n \"Failed to compare types!\" +\n higherType._def.typeName +\n \" \" +\n lowerType._def.typeName,\n );\n};\n"],"names":["DEFAULT_COMPARE_TYPE_OPTIONS","isPrimitiveType","a","z","isSameType","b","options","opts","interceptorResult","aShape","bShape","key","i","optionsA","optionsB","enumA","enumB","isCompatibleType","higherType","lowerType","option","superTypeShape","subTypeShape"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zod-compare",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Compare two Zod schemas recursively.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"zod",
|
|
7
|
+
"compare",
|
|
8
|
+
"comparator",
|
|
9
|
+
"schema",
|
|
10
|
+
"type",
|
|
11
|
+
"typescript",
|
|
12
|
+
"validation"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/lawvs/zod-compare.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "whitewater <me@waterwater.moe>",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@changesets/cli": "^2.27.1",
|
|
29
|
+
"@vitest/coverage-v8": "^1.3.1",
|
|
30
|
+
"prettier": "^3.2.5",
|
|
31
|
+
"typescript": "^5.3.3",
|
|
32
|
+
"vite": "^5.1.4",
|
|
33
|
+
"vite-plugin-dts": "^3.7.3",
|
|
34
|
+
"vitest": "^1.3.1",
|
|
35
|
+
"zod": "^3.22.4"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"zod": "^3.22.4"
|
|
39
|
+
},
|
|
40
|
+
"packageManager": "pnpm@8.15.3",
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"prettier": {},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc && vite build",
|
|
50
|
+
"format": "prettier --check .",
|
|
51
|
+
"format:fix": "prettier --write .",
|
|
52
|
+
"typeCheck": "tsc --noEmit",
|
|
53
|
+
"test": "vitest",
|
|
54
|
+
"changeset": "changeset",
|
|
55
|
+
"release": "pnpm run build && changeset publish"
|
|
56
|
+
},
|
|
57
|
+
"module": "./dist/index.js",
|
|
58
|
+
"types": "./dist/index.d.ts",
|
|
59
|
+
"exports": {
|
|
60
|
+
"types": "./dist/index.d.ts",
|
|
61
|
+
"import": "./dist/index.js",
|
|
62
|
+
"require": "./dist/index.cjs"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { isCompatibleType } from "../is-compatible-type.ts";
|
|
4
|
+
|
|
5
|
+
describe("isCompatibleType", () => {
|
|
6
|
+
test("should ref same type", () => {
|
|
7
|
+
const uniqueType = z.string().brand("unique");
|
|
8
|
+
expect(isCompatibleType(uniqueType, uniqueType)).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("compare basic type", () => {
|
|
12
|
+
expect(isCompatibleType(z.undefined(), z.undefined())).toBe(true);
|
|
13
|
+
expect(isCompatibleType(z.string(), z.string())).toBe(true);
|
|
14
|
+
expect(isCompatibleType(z.string(), z.number())).toBe(false);
|
|
15
|
+
expect(isCompatibleType(z.string().optional(), z.string())).toBe(false);
|
|
16
|
+
expect(isCompatibleType(z.string().optional(), z.string().optional())).toBe(
|
|
17
|
+
true,
|
|
18
|
+
);
|
|
19
|
+
expect(isCompatibleType(z.string().nullable(), z.string().optional())).toBe(
|
|
20
|
+
false,
|
|
21
|
+
);
|
|
22
|
+
expect(isCompatibleType(z.string(), z.string().nullable())).toBe(true);
|
|
23
|
+
expect(isCompatibleType(z.string(), z.string().optional())).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should compare simple object", () => {
|
|
27
|
+
expect(
|
|
28
|
+
isCompatibleType(
|
|
29
|
+
z.object({
|
|
30
|
+
name: z.string(),
|
|
31
|
+
other: z.number(),
|
|
32
|
+
}),
|
|
33
|
+
z.object({
|
|
34
|
+
name: z.string(),
|
|
35
|
+
}),
|
|
36
|
+
),
|
|
37
|
+
).toBe(true);
|
|
38
|
+
expect(
|
|
39
|
+
isCompatibleType(
|
|
40
|
+
z.object({
|
|
41
|
+
name: z.number(),
|
|
42
|
+
}),
|
|
43
|
+
z.object({
|
|
44
|
+
name: z.number().nullable(),
|
|
45
|
+
}),
|
|
46
|
+
),
|
|
47
|
+
).toBe(true);
|
|
48
|
+
expect(
|
|
49
|
+
isCompatibleType(
|
|
50
|
+
z
|
|
51
|
+
.object({
|
|
52
|
+
name: z.string().optional(),
|
|
53
|
+
})
|
|
54
|
+
.partial(),
|
|
55
|
+
z.object({
|
|
56
|
+
name: z.string(),
|
|
57
|
+
}),
|
|
58
|
+
),
|
|
59
|
+
).toBe(false);
|
|
60
|
+
expect(
|
|
61
|
+
isCompatibleType(
|
|
62
|
+
z.object({
|
|
63
|
+
name: z.string(),
|
|
64
|
+
}),
|
|
65
|
+
z
|
|
66
|
+
.object({
|
|
67
|
+
name: z.string(),
|
|
68
|
+
})
|
|
69
|
+
.partial(),
|
|
70
|
+
),
|
|
71
|
+
).toBe(true);
|
|
72
|
+
expect(
|
|
73
|
+
isCompatibleType(
|
|
74
|
+
z.object({
|
|
75
|
+
name: z.string().nullable(),
|
|
76
|
+
}),
|
|
77
|
+
z.object({
|
|
78
|
+
name: z.string().optional(),
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
81
|
+
).toBe(false);
|
|
82
|
+
expect(
|
|
83
|
+
isCompatibleType(
|
|
84
|
+
z.object({}),
|
|
85
|
+
z.object({
|
|
86
|
+
name: z.string(),
|
|
87
|
+
}),
|
|
88
|
+
),
|
|
89
|
+
).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("should compare rest tuple", () => {
|
|
93
|
+
expect(
|
|
94
|
+
isCompatibleType(
|
|
95
|
+
z.tuple([z.string(), z.string()]),
|
|
96
|
+
z.tuple([z.string()]),
|
|
97
|
+
),
|
|
98
|
+
).toBe(true);
|
|
99
|
+
expect(
|
|
100
|
+
isCompatibleType(
|
|
101
|
+
z.tuple([z.string(), z.string()]),
|
|
102
|
+
z.tuple([z.string(), z.number()]),
|
|
103
|
+
),
|
|
104
|
+
).toBe(false);
|
|
105
|
+
expect(
|
|
106
|
+
isCompatibleType(
|
|
107
|
+
z.tuple([z.string(), z.string()]).rest(z.number()),
|
|
108
|
+
z.tuple([z.string(), z.string()]),
|
|
109
|
+
),
|
|
110
|
+
).toBe(true);
|
|
111
|
+
expect(
|
|
112
|
+
isCompatibleType(
|
|
113
|
+
z.tuple([z.string()]),
|
|
114
|
+
z.tuple([z.string()]).rest(z.number()),
|
|
115
|
+
),
|
|
116
|
+
).toBe(false);
|
|
117
|
+
|
|
118
|
+
expect(
|
|
119
|
+
isCompatibleType(z.tuple([]).rest(z.number()), z.array(z.number())),
|
|
120
|
+
).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("should compare `or` type", () => {
|
|
124
|
+
expect(isCompatibleType(z.string(), z.string().or(z.number()))).toBe(true);
|
|
125
|
+
expect(
|
|
126
|
+
isCompatibleType(z.string(), z.number().or(z.string()).or(z.boolean())),
|
|
127
|
+
).toBe(true);
|
|
128
|
+
expect(isCompatibleType(z.string().or(z.number()), z.string())).toBe(false);
|
|
129
|
+
expect(
|
|
130
|
+
isCompatibleType(
|
|
131
|
+
z.string().or(z.number()),
|
|
132
|
+
z.number().or(z.string()).or(z.boolean()),
|
|
133
|
+
),
|
|
134
|
+
).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { isSameType } from "../is-same-type.ts";
|
|
4
|
+
|
|
5
|
+
describe("isSameType", () => {
|
|
6
|
+
test("should ref same type", () => {
|
|
7
|
+
const uniqueType = z.string().brand("unique");
|
|
8
|
+
expect(isSameType(uniqueType, uniqueType)).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("compare basic type", () => {
|
|
12
|
+
expect(isSameType(z.undefined(), z.undefined())).toBe(true);
|
|
13
|
+
expect(isSameType(z.string(), z.string())).toBe(true);
|
|
14
|
+
expect(isSameType(z.string(), z.number())).toBe(false);
|
|
15
|
+
expect(isSameType(z.string().optional(), z.string())).toBe(false);
|
|
16
|
+
expect(isSameType(z.string().optional(), z.string().optional())).toBe(true);
|
|
17
|
+
expect(isSameType(z.string().nullable(), z.string().optional())).toBe(
|
|
18
|
+
false,
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("should return false when compare branded type", () => {
|
|
23
|
+
expect(isSameType(z.string().brand("test"), z.string())).toBe(false);
|
|
24
|
+
expect(isSameType(z.string().brand("test"), z.string().brand("test"))).toBe(
|
|
25
|
+
false,
|
|
26
|
+
);
|
|
27
|
+
// expect(() =>
|
|
28
|
+
// isSameType(z.string().brand("test"), z.string().brand("test"))
|
|
29
|
+
// ).toThrowError();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("should compare simple object", () => {
|
|
33
|
+
expect(
|
|
34
|
+
isSameType(
|
|
35
|
+
z.object({
|
|
36
|
+
name: z.string(),
|
|
37
|
+
}),
|
|
38
|
+
z.object({
|
|
39
|
+
name: z.string(),
|
|
40
|
+
}),
|
|
41
|
+
),
|
|
42
|
+
).toBe(true);
|
|
43
|
+
expect(
|
|
44
|
+
isSameType(
|
|
45
|
+
z.object({
|
|
46
|
+
name: z.string(),
|
|
47
|
+
}),
|
|
48
|
+
z.object({
|
|
49
|
+
name: z.number(),
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
).toBe(false);
|
|
53
|
+
expect(
|
|
54
|
+
isSameType(
|
|
55
|
+
z
|
|
56
|
+
.object({
|
|
57
|
+
name: z.string(),
|
|
58
|
+
})
|
|
59
|
+
.partial(),
|
|
60
|
+
z.object({
|
|
61
|
+
name: z.string(),
|
|
62
|
+
}),
|
|
63
|
+
),
|
|
64
|
+
).toBe(false);
|
|
65
|
+
expect(
|
|
66
|
+
isSameType(
|
|
67
|
+
z
|
|
68
|
+
.object({
|
|
69
|
+
name: z.string(),
|
|
70
|
+
})
|
|
71
|
+
.partial(),
|
|
72
|
+
z
|
|
73
|
+
.object({
|
|
74
|
+
name: z.string(),
|
|
75
|
+
})
|
|
76
|
+
.partial(),
|
|
77
|
+
),
|
|
78
|
+
).toBe(true);
|
|
79
|
+
expect(
|
|
80
|
+
isSameType(
|
|
81
|
+
z.object({
|
|
82
|
+
name: z.string().nullable(),
|
|
83
|
+
}),
|
|
84
|
+
z.object({
|
|
85
|
+
name: z.string().optional(),
|
|
86
|
+
}),
|
|
87
|
+
),
|
|
88
|
+
).toBe(false);
|
|
89
|
+
expect(
|
|
90
|
+
isSameType(
|
|
91
|
+
z.object({}),
|
|
92
|
+
z.object({
|
|
93
|
+
name: z.string(),
|
|
94
|
+
}),
|
|
95
|
+
),
|
|
96
|
+
).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("should compare rest tuple", () => {
|
|
100
|
+
expect(
|
|
101
|
+
isSameType(
|
|
102
|
+
z.tuple([z.string(), z.string()]),
|
|
103
|
+
z.tuple([z.string(), z.string()]),
|
|
104
|
+
),
|
|
105
|
+
).toBe(true);
|
|
106
|
+
expect(
|
|
107
|
+
isSameType(
|
|
108
|
+
z.tuple([z.string(), z.string()]),
|
|
109
|
+
z.tuple([z.string(), z.number()]),
|
|
110
|
+
),
|
|
111
|
+
).toBe(false);
|
|
112
|
+
expect(
|
|
113
|
+
isSameType(
|
|
114
|
+
z.tuple([z.string(), z.string()]).rest(z.number()),
|
|
115
|
+
z.tuple([z.string(), z.string()]),
|
|
116
|
+
),
|
|
117
|
+
).toBe(false);
|
|
118
|
+
expect(
|
|
119
|
+
isSameType(
|
|
120
|
+
z.tuple([z.string(), z.string()]).rest(z.number()),
|
|
121
|
+
z.tuple([z.string(), z.string()]).rest(z.number()),
|
|
122
|
+
),
|
|
123
|
+
).toBe(true);
|
|
124
|
+
|
|
125
|
+
expect(isSameType(z.tuple([]).rest(z.number()), z.array(z.number()))).toBe(
|
|
126
|
+
false,
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("should compare `and` type", () => {
|
|
131
|
+
expect(
|
|
132
|
+
isSameType(z.string().and(z.number()), z.string().and(z.number())),
|
|
133
|
+
).toBe(true);
|
|
134
|
+
expect(
|
|
135
|
+
isSameType(z.number().and(z.string()), z.number().and(z.string())),
|
|
136
|
+
).toBe(true);
|
|
137
|
+
|
|
138
|
+
// order matters
|
|
139
|
+
expect(
|
|
140
|
+
isSameType(z.number().and(z.string()), z.string().and(z.number())),
|
|
141
|
+
).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("should compare `or` type", () => {
|
|
145
|
+
expect(
|
|
146
|
+
isSameType(z.string().or(z.number()), z.string().or(z.number())),
|
|
147
|
+
).toBe(true);
|
|
148
|
+
expect(
|
|
149
|
+
isSameType(z.number().or(z.string()), z.number().or(z.string())),
|
|
150
|
+
).toBe(true);
|
|
151
|
+
|
|
152
|
+
// order matters
|
|
153
|
+
expect(
|
|
154
|
+
isSameType(z.number().or(z.string()), z.string().or(z.number())),
|
|
155
|
+
).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("should compare literal type", () => {
|
|
159
|
+
expect(isSameType(z.literal("test"), z.literal("test"))).toBe(true);
|
|
160
|
+
expect(isSameType(z.literal("test"), z.literal("test2"))).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("should compare readonly type", () => {
|
|
164
|
+
expect(
|
|
165
|
+
isSameType(
|
|
166
|
+
z.object({ foo: z.string().readonly() }),
|
|
167
|
+
z.object({
|
|
168
|
+
foo: z.string().readonly(),
|
|
169
|
+
}),
|
|
170
|
+
),
|
|
171
|
+
).toBe(true);
|
|
172
|
+
expect(isSameType(z.string().readonly(), z.string())).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { z, type ZodType } from "zod";
|
|
2
|
+
import { isSameType } from "./is-same-type.ts";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_COMPARE_TYPE_OPTIONS,
|
|
5
|
+
type IsCompatibleTypeOptions,
|
|
6
|
+
} from "./utils.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if a the higherType matches the lowerType
|
|
10
|
+
*
|
|
11
|
+
* @deprecated This a unstable API and still in development
|
|
12
|
+
*
|
|
13
|
+
* @param higherType The looser type
|
|
14
|
+
* @param lowerType The stricter type
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* isCompatibleType(z.string(), z.string()); // true
|
|
19
|
+
*
|
|
20
|
+
* isCompatibleType(
|
|
21
|
+
* z.object({ name: z.string(), other: z.number() }),
|
|
22
|
+
* z.object({ name: z.string() })
|
|
23
|
+
* );
|
|
24
|
+
* // true
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const isCompatibleType = (
|
|
28
|
+
higherType: ZodType,
|
|
29
|
+
lowerType: ZodType,
|
|
30
|
+
options?: Partial<IsCompatibleTypeOptions>,
|
|
31
|
+
): boolean => {
|
|
32
|
+
const opts: IsCompatibleTypeOptions = {
|
|
33
|
+
...DEFAULT_COMPARE_TYPE_OPTIONS,
|
|
34
|
+
...options,
|
|
35
|
+
};
|
|
36
|
+
const interceptorResult = opts.interceptor(higherType, lowerType, opts);
|
|
37
|
+
if (interceptorResult === true || interceptorResult === false) {
|
|
38
|
+
return interceptorResult;
|
|
39
|
+
}
|
|
40
|
+
if (isSameType(higherType, lowerType, opts)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!("typeName" in higherType._def) || !("typeName" in lowerType._def)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Failed to compare type! " + higherType._def + " " + lowerType._def,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (
|
|
51
|
+
lowerType instanceof z.ZodOptional ||
|
|
52
|
+
lowerType instanceof z.ZodNullable
|
|
53
|
+
) {
|
|
54
|
+
return isCompatibleType(higherType, lowerType.unwrap(), opts);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ZodUnion aka or
|
|
58
|
+
if (higherType instanceof z.ZodUnion && lowerType instanceof z.ZodUnion) {
|
|
59
|
+
for (let i = 0; i < higherType.options.length; i++) {
|
|
60
|
+
const match = lowerType.options.some((option: ZodType) =>
|
|
61
|
+
isCompatibleType(option, lowerType.options[i], opts),
|
|
62
|
+
);
|
|
63
|
+
if (!match) return false;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
if (higherType instanceof z.ZodUnion) {
|
|
68
|
+
return higherType.options.every((option: ZodType) =>
|
|
69
|
+
isCompatibleType(option, lowerType, opts),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
if (lowerType instanceof z.ZodUnion) {
|
|
73
|
+
return lowerType.options.some((option: ZodType) =>
|
|
74
|
+
isCompatibleType(higherType, option, opts),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// compare constructor
|
|
79
|
+
if (higherType.constructor !== lowerType.constructor) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ZodObject
|
|
84
|
+
if (higherType instanceof z.ZodObject && lowerType instanceof z.ZodObject) {
|
|
85
|
+
const superTypeShape = higherType.shape;
|
|
86
|
+
const subTypeShape = lowerType.shape;
|
|
87
|
+
if (Object.keys(superTypeShape).length < Object.keys(subTypeShape).length)
|
|
88
|
+
return false;
|
|
89
|
+
for (const key in subTypeShape) {
|
|
90
|
+
if (!(key in superTypeShape)) return false;
|
|
91
|
+
if (!isCompatibleType(superTypeShape[key], subTypeShape[key], opts)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ZodArray
|
|
99
|
+
if (higherType instanceof z.ZodArray && lowerType instanceof z.ZodArray) {
|
|
100
|
+
return isCompatibleType(higherType.element, lowerType.element, opts);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ZodTuple
|
|
104
|
+
if (higherType instanceof z.ZodTuple && lowerType instanceof z.ZodTuple) {
|
|
105
|
+
if (higherType.items.length < lowerType.items.length) return false;
|
|
106
|
+
for (let i = 0; i < lowerType.items.length; i++) {
|
|
107
|
+
if (!isCompatibleType(higherType.items[i], lowerType.items[i], opts)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Check rest
|
|
112
|
+
if (lowerType._def.rest) {
|
|
113
|
+
if (!higherType._def.rest) return false;
|
|
114
|
+
return isCompatibleType(higherType._def.rest, lowerType._def.rest, opts);
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw new Error(
|
|
120
|
+
"Failed to compare types!" +
|
|
121
|
+
higherType._def.typeName +
|
|
122
|
+
" " +
|
|
123
|
+
lowerType._def.typeName,
|
|
124
|
+
);
|
|
125
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { EnumLike, ZodType } from "zod";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
type IsSameTypeOptions,
|
|
5
|
+
DEFAULT_COMPARE_TYPE_OPTIONS,
|
|
6
|
+
isPrimitiveType,
|
|
7
|
+
} from "./utils.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* isSameType is a function that checks if two ZodTypes are the same.
|
|
11
|
+
*
|
|
12
|
+
* Caveats:
|
|
13
|
+
* - The function does not validate specific criteria such as min or max values, length, email, etc.
|
|
14
|
+
* - It excludes comparisons involving methods like .describe(), .catch(), .default(), .refine(), and .transform().
|
|
15
|
+
* - When comparing definitions with .or and .and, they are assessed sequentially based on their order.
|
|
16
|
+
*
|
|
17
|
+
* @param a - The first ZodType to compare.
|
|
18
|
+
* @param b - The second ZodType to compare.
|
|
19
|
+
* @returns A boolean indicating whether the two types are the same.
|
|
20
|
+
*
|
|
21
|
+
* @throws Will throw an error if it encounters an unknown type.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* isSameType(z.string(), z.string()); // true
|
|
26
|
+
* isSameType(z.string(), z.number()); // false
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export const isSameType = (
|
|
30
|
+
a: ZodType,
|
|
31
|
+
b: ZodType,
|
|
32
|
+
options?: Partial<IsSameTypeOptions>,
|
|
33
|
+
): boolean => {
|
|
34
|
+
const opts = { ...DEFAULT_COMPARE_TYPE_OPTIONS, ...options };
|
|
35
|
+
const interceptorResult = opts.interceptor(a, b, opts);
|
|
36
|
+
if (interceptorResult === true || interceptorResult === false) {
|
|
37
|
+
return interceptorResult;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (a === undefined || b === undefined) {
|
|
41
|
+
throw new Error("Failed to compare type! " + a + " " + b);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (a === b) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// compare constructor
|
|
49
|
+
if (a.constructor !== b.constructor) {
|
|
50
|
+
// https://stackoverflow.com/questions/24959862/how-to-tell-if-two-javascript-instances-are-of-the-same-class-type
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// See https://github.com/colinhacks/zod/blob/master/src/types.ts
|
|
54
|
+
if (!("typeName" in a._def) || !("typeName" in b._def)) {
|
|
55
|
+
throw new Error("Failed to compare type! " + a._def + " " + b._def);
|
|
56
|
+
}
|
|
57
|
+
if (a._def.typeName !== b._def.typeName) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ZodBranded
|
|
62
|
+
if (opts.ignoreBranded) {
|
|
63
|
+
if (a instanceof z.ZodBranded) {
|
|
64
|
+
a = a.unwrap();
|
|
65
|
+
}
|
|
66
|
+
if (b instanceof z.ZodBranded) {
|
|
67
|
+
b = b.unwrap();
|
|
68
|
+
}
|
|
69
|
+
return isSameType(a, b, opts);
|
|
70
|
+
} else {
|
|
71
|
+
if (a instanceof z.ZodBranded || b instanceof z.ZodBranded) {
|
|
72
|
+
// We can not distinguish different branded type
|
|
73
|
+
// throw new Error("Can not distinguish different branded type!");
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ZodPromise ZodOptional ZodNullable ZodBranded
|
|
79
|
+
if ("unwrap" in a && typeof a.unwrap === "function") {
|
|
80
|
+
if (!("unwrap" in b && typeof b.unwrap === "function")) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return isSameType(a.unwrap(), b.unwrap(), opts);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!opts.ignoreOptional && a.isOptional() !== b.isOptional()) return false;
|
|
87
|
+
if (!opts.ignoreNullable && a.isNullable() !== b.isNullable()) return false;
|
|
88
|
+
|
|
89
|
+
if (isPrimitiveType(a)) {
|
|
90
|
+
// Already assert a and b are the same constructor
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ZodObject
|
|
95
|
+
if (a instanceof z.ZodObject && b instanceof z.ZodObject) {
|
|
96
|
+
const aShape = a.shape;
|
|
97
|
+
const bShape = b.shape;
|
|
98
|
+
if (Object.keys(aShape).length !== Object.keys(bShape).length) return false;
|
|
99
|
+
for (const key in aShape) {
|
|
100
|
+
if (!(key in bShape)) return false;
|
|
101
|
+
if (!isSameType(aShape[key], bShape[key], opts)) return false;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ZodArray
|
|
107
|
+
if (a instanceof z.ZodArray && b instanceof z.ZodArray) {
|
|
108
|
+
return isSameType(a.element, b.element, opts);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ZodTuple
|
|
112
|
+
if (a instanceof z.ZodTuple && b instanceof z.ZodTuple) {
|
|
113
|
+
if (a.items.length !== b.items.length) return false;
|
|
114
|
+
for (let i = 0; i < a.items.length; i++) {
|
|
115
|
+
if (!isSameType(a.items[i], b.items[i], opts)) return false;
|
|
116
|
+
}
|
|
117
|
+
// Compare rest
|
|
118
|
+
if (a._def.rest || b._def.rest) {
|
|
119
|
+
// If one has rest, the other must have rest
|
|
120
|
+
if (!a._def.rest || !b._def.rest) return false;
|
|
121
|
+
return isSameType(a._def.rest, b._def.rest, opts);
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ZodLiteral
|
|
127
|
+
if (a instanceof z.ZodLiteral && b instanceof z.ZodLiteral) {
|
|
128
|
+
return a.value === b.value;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ZodIntersection aka and
|
|
132
|
+
if (a instanceof z.ZodIntersection && b instanceof z.ZodIntersection) {
|
|
133
|
+
return (
|
|
134
|
+
isSameType(a._def.left, b._def.left, opts) &&
|
|
135
|
+
isSameType(a._def.right, b._def.right, opts)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ZodUnion aka or
|
|
140
|
+
if (a instanceof z.ZodUnion && b instanceof z.ZodUnion) {
|
|
141
|
+
if (a.options.length !== b.options.length) return false;
|
|
142
|
+
for (let i = 0; i < a.options.length; i++) {
|
|
143
|
+
if (!isSameType(a.options[i], b.options[i], opts)) return false;
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ZodReadonly
|
|
149
|
+
if (a instanceof z.ZodReadonly && b instanceof z.ZodReadonly) {
|
|
150
|
+
return isSameType(a._def.innerType, b._def.innerType, opts);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ZodRecord / ZodMap
|
|
154
|
+
if (
|
|
155
|
+
(a instanceof z.ZodRecord && b instanceof z.ZodRecord) ||
|
|
156
|
+
(a instanceof z.ZodMap && b instanceof z.ZodMap)
|
|
157
|
+
) {
|
|
158
|
+
return (
|
|
159
|
+
isSameType(a.keySchema, b.keySchema, opts) &&
|
|
160
|
+
isSameType(a.valueSchema, b.valueSchema, opts)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ZodSet
|
|
165
|
+
if (a instanceof z.ZodSet && b instanceof z.ZodSet) {
|
|
166
|
+
return isSameType(a._def.valueType, b._def.valueType, opts);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ZodFunction
|
|
170
|
+
if (a instanceof z.ZodFunction && b instanceof z.ZodFunction) {
|
|
171
|
+
return (
|
|
172
|
+
isSameType(a.parameters(), b.parameters(), opts) &&
|
|
173
|
+
isSameType(a.returnType(), b.returnType(), opts)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ZodEnum
|
|
178
|
+
if (a instanceof z.ZodEnum && b instanceof z.ZodEnum) {
|
|
179
|
+
const optionsA: [string, ...string[]] = a.options;
|
|
180
|
+
const optionsB: [string, ...string[]] = b.options;
|
|
181
|
+
if (optionsA.length !== optionsB.length) return false;
|
|
182
|
+
for (let i = 0; i < optionsA.length; i++) {
|
|
183
|
+
if (optionsA[i] !== optionsB[i]) return false;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ZodNativeEnum
|
|
189
|
+
if (a instanceof z.ZodNativeEnum && b instanceof z.ZodNativeEnum) {
|
|
190
|
+
const enumA: EnumLike = a.enum;
|
|
191
|
+
const enumB: EnumLike = b.enum;
|
|
192
|
+
if (Object.keys(enumA).length !== Object.keys(enumB).length) return false;
|
|
193
|
+
for (const key in enumA) {
|
|
194
|
+
if (enumA[key] !== enumB[key]) return false;
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ZodLazy
|
|
200
|
+
// ZodEffects
|
|
201
|
+
// ZodDefault
|
|
202
|
+
// ZodCatch
|
|
203
|
+
// ZodPipeline
|
|
204
|
+
// ZodTransformer
|
|
205
|
+
// ZodError
|
|
206
|
+
console.error('Failed to compare type! "' + a, b);
|
|
207
|
+
throw new Error("Unknown type! " + a._def.typeName + " " + b._def.typeName);
|
|
208
|
+
};
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z, type ZodType } from "zod";
|
|
2
|
+
|
|
3
|
+
export interface IsSameTypeOptions {
|
|
4
|
+
deep: true;
|
|
5
|
+
/**
|
|
6
|
+
* Ignore all specific validations like min, max, length, email etc.
|
|
7
|
+
*
|
|
8
|
+
* You still can use `interceptor` to validate the type manually.
|
|
9
|
+
*/
|
|
10
|
+
ignoreValidations: true;
|
|
11
|
+
ignoreOptional: boolean;
|
|
12
|
+
ignoreNullable: boolean;
|
|
13
|
+
ignoreReadOnly: false;
|
|
14
|
+
ignoreBranded: boolean;
|
|
15
|
+
ignoreIntersectionsOrder: false;
|
|
16
|
+
/**
|
|
17
|
+
* A function that provides custom logic for comparing two ZodType instances.
|
|
18
|
+
*
|
|
19
|
+
* If the function returns `true` or `false`, the result will be used as the comparison result.
|
|
20
|
+
* Otherwise, the default comparison logic will be used.
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
interceptor: (
|
|
24
|
+
a: ZodType,
|
|
25
|
+
b: ZodType,
|
|
26
|
+
options: IsSameTypeOptions,
|
|
27
|
+
) => boolean | void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IsCompatibleTypeOptions extends IsSameTypeOptions {
|
|
31
|
+
ignoreOptional: false;
|
|
32
|
+
ignoreNullable: false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const DEFAULT_COMPARE_TYPE_OPTIONS = {
|
|
36
|
+
deep: true,
|
|
37
|
+
ignoreOptional: false,
|
|
38
|
+
ignoreNullable: false,
|
|
39
|
+
ignoreReadOnly: false,
|
|
40
|
+
ignoreBranded: false,
|
|
41
|
+
ignoreValidations: true,
|
|
42
|
+
ignoreIntersectionsOrder: false,
|
|
43
|
+
interceptor: () => {},
|
|
44
|
+
} as const satisfies IsSameTypeOptions;
|
|
45
|
+
|
|
46
|
+
// See ZodFirstPartyTypeKind
|
|
47
|
+
export const isPrimitiveType = (a: ZodType): boolean => {
|
|
48
|
+
return (
|
|
49
|
+
a instanceof z.ZodString ||
|
|
50
|
+
a instanceof z.ZodNumber ||
|
|
51
|
+
a instanceof z.ZodNaN ||
|
|
52
|
+
a instanceof z.ZodBigInt ||
|
|
53
|
+
a instanceof z.ZodBoolean ||
|
|
54
|
+
a instanceof z.ZodDate ||
|
|
55
|
+
a instanceof z.ZodSymbol ||
|
|
56
|
+
a instanceof z.ZodUndefined ||
|
|
57
|
+
a instanceof z.ZodNull ||
|
|
58
|
+
a instanceof z.ZodAny ||
|
|
59
|
+
a instanceof z.ZodUnknown ||
|
|
60
|
+
a instanceof z.ZodNever ||
|
|
61
|
+
a instanceof z.ZodVoid
|
|
62
|
+
);
|
|
63
|
+
};
|