Skip to content

Commit

Permalink
Merge pull request #180 from ThomasAribart/make-defaulted-properties-…
Browse files Browse the repository at this point in the history
…required

make defaulted properties required
  • Loading branch information
ThomasAribart authored Dec 20, 2023
2 parents b043c8e + 8b519bc commit 560372f
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 6 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,27 @@ type Object = FromSchema<typeof objectSchema>;
// => { [x: string]: unknown; foo: string; bar?: number; }
```

Defaulted properties (even optional ones) will be set as required in the resulting type. You can turn off this behavior by setting the `keepDefaultedPropertiesOptional` option to `true`:

```typescript
const defaultedProp = {
type: "object",
properties: {
foo: { type: "string", default: "bar" },
},
additionalProperties: false,
} as const;

type Object = FromSchema<typeof defaultedProp>;
// => { foo: string; }

type Object = FromSchema<
typeof defaultedProp,
{ keepDefaultedPropertiesOptional: true }
>;
// => { foo?: string; }
```

`FromSchema` partially supports the `additionalProperties` and `patternProperties` keywords:

- `additionalProperties` can be used to deny additional properties.
Expand Down
3 changes: 3 additions & 0 deletions src/definitions/fromSchemaOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
export type FromSchemaOptions = {
parseNotKeyword?: boolean;
parseIfThenElseKeywords?: boolean;
keepDefaultedPropertiesOptional?: boolean;
references?: JSONSchema7Reference[] | false;
deserialize?: DeserializationPattern[] | false;
};
Expand All @@ -23,6 +24,7 @@ export type FromExtendedSchemaOptions<EXTENSION extends JSONSchema7Extension> =
{
parseNotKeyword?: boolean;
parseIfThenElseKeywords?: boolean;
keepDefaultedPropertiesOptional?: boolean;
references?: ExtendedJSONSchema7Reference<EXTENSION>[] | false;
deserialize?: DeserializationPattern[] | false;
};
Expand All @@ -33,6 +35,7 @@ export type FromExtendedSchemaOptions<EXTENSION extends JSONSchema7Extension> =
export type FromSchemaDefaultOptions = {
parseNotKeyword: false;
parseIfThenElseKeywords: false;
keepDefaultedPropertiesOptional: false;
references: false;
deserialize: false;
};
3 changes: 3 additions & 0 deletions src/parse-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export type ParseOptions<
parseIfThenElseKeywords: OPTIONS["parseIfThenElseKeywords"] extends boolean
? OPTIONS["parseIfThenElseKeywords"]
: FromSchemaDefaultOptions["parseIfThenElseKeywords"];
keepDefaultedPropertiesOptional: OPTIONS["keepDefaultedPropertiesOptional"] extends boolean
? OPTIONS["keepDefaultedPropertiesOptional"]
: FromSchemaDefaultOptions["keepDefaultedPropertiesOptional"];
rootSchema: ROOT_SCHEMA;
references: OPTIONS["references"] extends JSONSchema7Reference[]
? IndexReferencesById<OPTIONS["references"]>
Expand Down
1 change: 1 addition & 0 deletions src/parse-options.type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type ReceivedOptions = ParseOptions<RootSchema, { references: AllReferences }>;
type ExpectedOptions = {
parseNotKeyword: FromSchemaDefaultOptions["parseNotKeyword"];
parseIfThenElseKeywords: FromSchemaDefaultOptions["parseIfThenElseKeywords"];
keepDefaultedPropertiesOptional: FromSchemaDefaultOptions["keepDefaultedPropertiesOptional"];
deserialize: FromSchemaDefaultOptions["deserialize"];
rootSchema: RootSchema;
references: IndexReferencesById<AllReferences>;
Expand Down
4 changes: 4 additions & 0 deletions src/parse-schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export type ParseSchemaOptions = {
* Wether to parse ifThenElse schemas or not (false by default)
*/
parseIfThenElseKeywords: boolean;
/**
* Wether to keep object defaulted properties optional or not (false by default)
*/
keepDefaultedPropertiesOptional: boolean;
/**
* The initial schema provided to `ParseSchema`
*/
Expand Down
29 changes: 23 additions & 6 deletions src/parse-schema/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ export type ParseObjectSchema<
OPTIONS
>;
},
GetRequired<OBJECT_SCHEMA>,
GetRequired<OBJECT_SCHEMA, OPTIONS>,
GetOpenProps<OBJECT_SCHEMA, OPTIONS>
>
: M.$Object<
{},
GetRequired<OBJECT_SCHEMA>,
GetRequired<OBJECT_SCHEMA, OPTIONS>,
GetOpenProps<OBJECT_SCHEMA, OPTIONS>
>;

Expand All @@ -53,10 +53,27 @@ export type ParseObjectSchema<
* @param OBJECT_SCHEMA JSONSchema (object type)
* @returns String
*/
type GetRequired<OBJECT_SCHEMA extends ObjectSchema> =
OBJECT_SCHEMA extends Readonly<{ required: ReadonlyArray<string> }>
? OBJECT_SCHEMA["required"][number]
: never;
type GetRequired<
OBJECT_SCHEMA extends ObjectSchema,
OPTIONS extends ParseSchemaOptions,
> =
| (OBJECT_SCHEMA extends Readonly<{ required: ReadonlyArray<string> }>
? OBJECT_SCHEMA["required"][number]
: never)
| (OPTIONS["keepDefaultedPropertiesOptional"] extends true
? never
: OBJECT_SCHEMA extends Readonly<{
properties: Readonly<Record<string, JSONSchema7>>;
}>
? {
[KEY in keyof OBJECT_SCHEMA["properties"] &
string]: OBJECT_SCHEMA["properties"][KEY] extends Readonly<{
default: unknown;
}>
? KEY
: never;
}[keyof OBJECT_SCHEMA["properties"] & string]
: never);

/**
* Extracts and parses the additional and pattern properties (if any exists) of an object JSON schema
Expand Down
33 changes: 33 additions & 0 deletions src/tests/readme/object.type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,36 @@ type AssertObjectWithTypedAdditionalProperties = A.Equals<
>;
const assertObjectWithTypedAdditionalProperties: AssertObjectWithTypedAdditionalProperties = 1;
assertObjectWithTypedAdditionalProperties;

// Defaulted property

const objectWithDefaultedPropertySchema = {
type: "object",
properties: {
foo: { type: "string", default: "bar" },
},
additionalProperties: false,
} as const;

type ReceivedObjectWithDefaultedProperty = FromSchema<
typeof objectWithDefaultedPropertySchema
>;
type ExpectedObjectWithDefaultedProperty = { foo: string };
type AssertObjectWithDefaultedProperty = A.Equals<
ReceivedObjectWithDefaultedProperty,
ExpectedObjectWithDefaultedProperty
>;
const assertObjectWithDefaultedProperty: AssertObjectWithDefaultedProperty = 1;
assertObjectWithDefaultedProperty;

type ReceivedObjectWithDefaultedProperty2 = FromSchema<
typeof objectWithDefaultedPropertySchema,
{ keepDefaultedPropertiesOptional: true }
>;
type ExpectedObjectWithDefaultedProperty2 = { foo?: string };
type AssertObjectWithDefaultedProperty2 = A.Equals<
ReceivedObjectWithDefaultedProperty2,
ExpectedObjectWithDefaultedProperty2
>;
const assertObjectWithDefaultedProperty2: AssertObjectWithDefaultedProperty2 = 1;
assertObjectWithDefaultedProperty2;

0 comments on commit 560372f

Please sign in to comment.