diff --git a/src/mergeProps.ts b/src/mergeProps.ts index 22e2ecb..58edd9b 100644 --- a/src/mergeProps.ts +++ b/src/mergeProps.ts @@ -6,20 +6,23 @@ import { isJSONSchemaObjectType } from './utils'; /** * Merge and dedupe 2 tuples */ -type MergeTuples = readonly [ - ...UnionToTuple | TupleToUnion>, -]; +type MergeTuples = Readonly< + UnionToTuple | TupleToUnion> +>; +/** + * Merge two optional records, keeping `undefined` if both are undefined + */ type MergeOptionalRecords< - Object1 extends Record | undefined, - Object2 extends Record | undefined, + A extends Record | undefined, + B extends Record | undefined, > = - Object1 extends Record - ? Object2 extends Record - ? Merge - : Object1 - : Object2 extends Record - ? Object2 + A extends Record + ? B extends Record + ? Merge + : A + : B extends Record + ? B : undefined; type MergeProps< diff --git a/src/omitProps.ts b/src/omitProps.ts index bbcb0d0..f5e8d52 100644 --- a/src/omitProps.ts +++ b/src/omitProps.ts @@ -1,12 +1,12 @@ -import type { Merge, SetRequired, TupleToUnion, UnionToTuple } from 'type-fest'; +import type { Merge, SetRequired } from 'type-fest'; -import type { JSONSchemaObject, JSONSchemaObjectOutput } from './types'; +import type { + JSONSchemaObject, + JSONSchemaObjectOutput, + OmitFromTuple, +} from './types'; import { isJSONSchemaObjectType } from './utils'; -type OmitFromTuple = Readonly< - UnionToTuple, EntriesToOmit>> ->; - type OmitSchemaProperties< Schema extends JSONSchemaObject, Keys extends (keyof Schema['properties'])[], @@ -16,11 +16,7 @@ type OmitSchemaProperties< properties: Omit; required: undefined extends Schema['required'] ? undefined - : OmitFromTuple< - // @ts-expect-error extends doesn't narrow type - Schema['required'], - Keys[number] - >; + : OmitFromTuple; } >; diff --git a/src/optionalProps.ts b/src/optionalProps.ts index e1f851d..292949a 100644 --- a/src/optionalProps.ts +++ b/src/optionalProps.ts @@ -1,6 +1,10 @@ -import type { Merge, TupleToUnion, UnionToTuple } from 'type-fest'; +import type { Merge } from 'type-fest'; -import type { JSONSchemaObject, JSONSchemaObjectOutput } from './types'; +import type { + JSONSchemaObject, + JSONSchemaObjectOutput, + OmitFromTuple, +} from './types'; import { isJSONSchemaObjectType } from './utils'; type ObjectKeys = string | number | symbol; @@ -15,11 +19,7 @@ type OptionalProps< Merge< Schema, { - required: Readonly< - UnionToTuple< - Exclude, TupleToUnion> - > - >; + required: OmitFromTuple[number]>; } >; /** diff --git a/src/sealSchema.ts b/src/sealSchema.ts index e78fe86..68fc416 100644 --- a/src/sealSchema.ts +++ b/src/sealSchema.ts @@ -3,21 +3,23 @@ import type { Merge } from 'type-fest'; import type { JSONSchema, JSONSchemaObjectOutput } from './types'; import { isObject } from './utils'; -type DisableAdditionalProperties = - 'type' extends keyof Value - ? Value['type'] extends 'object' - ? Merge> - : Value - : Value; - -type DisableAdditionalPropertiesDeep< - _Value extends object, - Value = DisableAdditionalProperties<_Value>, -> = { - [Key in keyof Value]: Value[Key] extends object - ? DisableAdditionalPropertiesDeep - : Value[Key]; -}; +type DisableAdditionalPropertiesDeep = Value extends object + ? Value extends { type: 'object' } + ? // JSON schema object type + Readonly< + Merge< + { + [Key in keyof Value]: DisableAdditionalPropertiesDeep; + }, + { additionalProperties: false } + > + > + : // Any other object/array + Readonly<{ + [Key in keyof Value]: DisableAdditionalPropertiesDeep; + }> + : // Any other primitive + Value; function disableAdditionalPropertiesDeep(item: unknown): unknown { if (isObject(item)) { diff --git a/src/types.ts b/src/types.ts index 92afe06..7de389e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,6 +3,8 @@ import type { OverrideProperties, SetRequired, Simplify, + TupleToUnion, + UnionToTuple, } from 'type-fest'; // https://github.com/ThomasAribart/json-schema-to-ts/blob/12767c1eab895159c01f5e6898d8e5e711ff9d1c/src/definitions/jsonSchema.ts @@ -27,3 +29,8 @@ export type JSONSchemaObjectOutput = Simplify< ConditionalExcept > >; + +export type OmitFromTuple< + Tuple extends readonly unknown[] | undefined, + EntriesToOmit, +> = Readonly, EntriesToOmit>>>; diff --git a/src/unsealSchema.ts b/src/unsealSchema.ts index 8ed963a..76c4908 100644 --- a/src/unsealSchema.ts +++ b/src/unsealSchema.ts @@ -1,20 +1,21 @@ import type { JSONSchema, JSONSchemaObjectOutput } from './types'; import { isObject } from './utils'; -type OmitAdditionalProperty = 'type' extends keyof Value - ? Value['type'] extends 'object' - ? Omit - : Value - : Value; - -type OmitAdditionalPropertiesDeep< - _Value extends object, - Value = OmitAdditionalProperty<_Value>, -> = { - [Key in keyof Value]: Value[Key] extends object - ? OmitAdditionalPropertiesDeep - : Value[Key]; -}; +type OmitAdditionalPropertiesDeep = Value extends object + ? Value extends { type: 'object' } + ? // JSON schema object type + Readonly<{ + [Key in keyof Omit< + Value, + 'additionalProperties' + >]: OmitAdditionalPropertiesDeep; + }> + : // Any other object/array + Readonly<{ + [Key in keyof Value]: OmitAdditionalPropertiesDeep; + }> + : // Any other primitive + Value; function omitAdditionalPropertiesDeep(item: unknown): unknown { if (isObject(item)) { diff --git a/tsconfig.json b/tsconfig.json index 32fd101..9530956 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,8 @@ "isolatedModules": true, "erasableSyntaxOnly": true, "noUncheckedIndexedAccess": true, - "noImplicitOverride": true + "noImplicitOverride": true, + "noErrorTruncation": true }, "exclude": ["./dist"] }