From 4d8a1db82fcc310bd62471745c2eb02379f992a9 Mon Sep 17 00:00:00 2001 From: Fabian Hiller Date: Fri, 23 Aug 2024 17:11:08 +0200 Subject: [PATCH] Add support for exactOptionalPropertyTypes #385 --- library/CHANGELOG.md | 4 + .../actions/partialCheck/partialCheck.test.ts | 2 - .../partialCheck/partialCheckAsync.test.ts | 2 - .../_isPartiallyTyped.test.ts | 1 - library/src/actions/rawCheck/types.ts | 12 +-- library/src/actions/rawTransform/types.ts | 12 +-- library/src/methods/partial/partial.test-d.ts | 52 ++++++------ .../methods/partial/partialAsync.test-d.ts | 52 ++++++------ .../src/methods/required/required.test-d.ts | 12 +-- .../methods/required/requiredAsync.test-d.ts | 12 +-- .../schemas/looseObject/looseObject.test-d.ts | 6 +- .../schemas/looseObject/looseObject.test.ts | 2 + .../looseObject/looseObjectAsync.test-d.ts | 6 +- .../looseObject/looseObjectAsync.test.ts | 15 +++- library/src/schemas/object/object.test-d.ts | 6 +- library/src/schemas/object/object.test.ts | 2 + .../src/schemas/object/objectAsync.test-d.ts | 6 +- .../src/schemas/object/objectAsync.test.ts | 2 + .../objectWithRest/objectWithRest.test-d.ts | 6 +- .../objectWithRestAsync.test-d.ts | 6 +- .../strictObject/strictObject.test-d.ts | 6 +- .../schemas/strictObject/strictObject.test.ts | 2 + .../strictObject/strictObjectAsync.test-d.ts | 6 +- .../strictObject/strictObjectAsync.test.ts | 15 +++- library/src/types/config.ts | 8 +- library/src/types/issue.ts | 6 +- library/src/types/object.ts | 81 +++++++++++++++---- library/src/utils/_addIssue/_addIssue.ts | 17 ++-- library/tsconfig.json | 13 +-- 29 files changed, 224 insertions(+), 148 deletions(-) diff --git a/library/CHANGELOG.md b/library/CHANGELOG.md index b2f3f7ce8..d8b945a3c 100644 --- a/library/CHANGELOG.md +++ b/library/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the library will be documented in this file. +## vX.X.X (Month DD, YYYY) + +- Add support for `exactOptionalPropertyTypes` config (issue #385) + ## v0.38.0 (August 20, 2024) - Change `expects` and `expected` property by enclosing combined values in parentheses diff --git a/library/src/actions/partialCheck/partialCheck.test.ts b/library/src/actions/partialCheck/partialCheck.test.ts index 0ad38ad61..f35238928 100644 --- a/library/src/actions/partialCheck/partialCheck.test.ts +++ b/library/src/actions/partialCheck/partialCheck.test.ts @@ -211,7 +211,6 @@ describe('partialCheck', () => { tuple: [123, { key: 'foo' }, 456], other: 'bar', }, - issues: undefined, }; expect(action._run(dataset, {})).toStrictEqual(dataset); }); @@ -227,7 +226,6 @@ describe('partialCheck', () => { const dataset: TypedDataset = { typed: true, value: input, - issues: undefined, }; expect(action._run(dataset, {})).toStrictEqual({ ...dataset, diff --git a/library/src/actions/partialCheck/partialCheckAsync.test.ts b/library/src/actions/partialCheck/partialCheckAsync.test.ts index 00ede68a9..fcf48bcb2 100644 --- a/library/src/actions/partialCheck/partialCheckAsync.test.ts +++ b/library/src/actions/partialCheck/partialCheckAsync.test.ts @@ -215,7 +215,6 @@ describe('partialCheckAsync', () => { tuple: [123, { key: 'foo' }, 456], other: 'bar', }, - issues: undefined, }; expect(await action._run(dataset, {})).toStrictEqual(dataset); }); @@ -231,7 +230,6 @@ describe('partialCheckAsync', () => { const dataset: TypedDataset = { typed: true, value: input, - issues: undefined, }; expect(await action._run(dataset, {})).toStrictEqual({ ...dataset, diff --git a/library/src/actions/partialCheck/utils/_isPartiallyTyped/_isPartiallyTyped.test.ts b/library/src/actions/partialCheck/utils/_isPartiallyTyped/_isPartiallyTyped.test.ts index 86390d825..923a73521 100644 --- a/library/src/actions/partialCheck/utils/_isPartiallyTyped/_isPartiallyTyped.test.ts +++ b/library/src/actions/partialCheck/utils/_isPartiallyTyped/_isPartiallyTyped.test.ts @@ -131,7 +131,6 @@ describe('_isPartiallyTyped', () => { const dataset: TypedDataset = { typed: true, value: input, - issues: undefined, }; expect(_isPartiallyTyped(dataset, pathList)).toBe(true); }); diff --git a/library/src/actions/rawCheck/types.ts b/library/src/actions/rawCheck/types.ts index 31917fe16..92932bbed 100644 --- a/library/src/actions/rawCheck/types.ts +++ b/library/src/actions/rawCheck/types.ts @@ -24,12 +24,12 @@ export interface RawCheckIssue extends BaseIssue { * Issue info type. */ interface IssueInfo { - label?: string; - input?: unknown; - expected?: string; - received?: string; - message?: ErrorMessage>; - path?: [IssuePathItem, ...IssuePathItem[]]; + label?: string | undefined; + input?: unknown | undefined; + expected?: string | undefined; + received?: string | undefined; + message?: ErrorMessage> | undefined; + path?: [IssuePathItem, ...IssuePathItem[]] | undefined; } /** diff --git a/library/src/actions/rawTransform/types.ts b/library/src/actions/rawTransform/types.ts index 7e76eba0a..60365f5e5 100644 --- a/library/src/actions/rawTransform/types.ts +++ b/library/src/actions/rawTransform/types.ts @@ -24,12 +24,12 @@ export interface RawTransformIssue extends BaseIssue { * Issue info type. */ interface IssueInfo { - label?: string; - input?: unknown; - expected?: string; - received?: string; - message?: ErrorMessage>; - path?: [IssuePathItem, ...IssuePathItem[]]; + label?: string | undefined; + input?: unknown | undefined; + expected?: string | undefined; + received?: string | undefined; + message?: ErrorMessage> | undefined; + path?: [IssuePathItem, ...IssuePathItem[]] | undefined; } /** diff --git a/library/src/methods/partial/partial.test-d.ts b/library/src/methods/partial/partial.test-d.ts index 06cbe26af..b12800f65 100644 --- a/library/src/methods/partial/partial.test-d.ts +++ b/library/src/methods/partial/partial.test-d.ts @@ -44,30 +44,30 @@ describe('partial', () => { describe('should infer correct types', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | null | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number | null; }>(); expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; + key1?: string; key2: number; - key3?: string | undefined; - key4?: number | null | undefined; + key3?: string; + key4?: number | null; }>(); }); test('of output', () => { expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number; }>(); expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; + key1?: string; key2: number; - key3?: string | undefined; + key3?: string; key4: number; }>(); }); @@ -105,18 +105,18 @@ describe('partial', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | null | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number | null; } & { [key: string]: boolean } >(); expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | null | undefined; + key2?: number; + key3?: string; + key4?: number | null; } & { [key: string]: boolean } >(); }); @@ -124,17 +124,17 @@ describe('partial', () => { test('of output', () => { expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number; } & { [key: string]: boolean } >(); expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: number | undefined; - key3?: string | undefined; + key2?: number; + key3?: string; key4: number; } & { [key: string]: boolean } >(); diff --git a/library/src/methods/partial/partialAsync.test-d.ts b/library/src/methods/partial/partialAsync.test-d.ts index 8f993a1b7..6a29c2ec4 100644 --- a/library/src/methods/partial/partialAsync.test-d.ts +++ b/library/src/methods/partial/partialAsync.test-d.ts @@ -44,30 +44,30 @@ describe('partialAsync', () => { describe('should infer correct types', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | null | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number | null; }>(); expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; + key1?: string; key2: number; - key3?: string | undefined; - key4?: number | null | undefined; + key3?: string; + key4?: number | null; }>(); }); test('of output', () => { expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number; }>(); expectTypeOf>().toEqualTypeOf<{ - key1?: string | undefined; + key1?: string; key2: number; - key3?: string | undefined; + key3?: string; key4: number; }>(); }); @@ -105,18 +105,18 @@ describe('partialAsync', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | null | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number | null; } & { [key: string]: boolean } >(); expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | null | undefined; + key2?: number; + key3?: string; + key4?: number | null; } & { [key: string]: boolean } >(); }); @@ -124,17 +124,17 @@ describe('partialAsync', () => { test('of output', () => { expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; - key2?: number | undefined; - key3?: string | undefined; - key4?: number | undefined; + key1?: string; + key2?: number; + key3?: string; + key4?: number; } & { [key: string]: boolean } >(); expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: number | undefined; - key3?: string | undefined; + key2?: number; + key3?: string; key4: number; } & { [key: string]: boolean } >(); diff --git a/library/src/methods/required/required.test-d.ts b/library/src/methods/required/required.test-d.ts index 617ed9813..bcde09654 100644 --- a/library/src/methods/required/required.test-d.ts +++ b/library/src/methods/required/required.test-d.ts @@ -55,9 +55,9 @@ describe('required', () => { }>(); expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: number | undefined; + key2?: number; key3: string; - key4?: number | null | undefined; + key4?: number | null; }>(); }); @@ -70,7 +70,7 @@ describe('required', () => { }>(); expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: number | undefined; + key2?: number; key3: string; key4: number; }>(); @@ -119,10 +119,10 @@ describe('required', () => { >(); expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; + key1?: string; key2: number; key3: string; - key4?: number | null | undefined; + key4?: number | null; } & { [key: string]: boolean } >(); }); @@ -138,7 +138,7 @@ describe('required', () => { >(); expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; + key1?: string; key2: number; key3: string; key4: number; diff --git a/library/src/methods/required/requiredAsync.test-d.ts b/library/src/methods/required/requiredAsync.test-d.ts index 0bb13556a..bd51e1382 100644 --- a/library/src/methods/required/requiredAsync.test-d.ts +++ b/library/src/methods/required/requiredAsync.test-d.ts @@ -63,9 +63,9 @@ describe('requiredAsync', () => { }>(); expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: number | undefined; + key2?: number; key3: string; - key4?: number | null | undefined; + key4?: number | null; }>(); }); @@ -78,7 +78,7 @@ describe('requiredAsync', () => { }>(); expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: number | undefined; + key2?: number; key3: string; key4: number; }>(); @@ -131,10 +131,10 @@ describe('requiredAsync', () => { >(); expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; + key1?: string; key2: number; key3: string; - key4?: number | null | undefined; + key4?: number | null; } & { [key: string]: boolean } >(); }); @@ -150,7 +150,7 @@ describe('requiredAsync', () => { >(); expectTypeOf>().toEqualTypeOf< { - key1?: string | undefined; + key1?: string; key2: number; key3: string; key4: number; diff --git a/library/src/schemas/looseObject/looseObject.test-d.ts b/library/src/schemas/looseObject/looseObject.test-d.ts index 7420df3c5..c18fd7ac4 100644 --- a/library/src/schemas/looseObject/looseObject.test-d.ts +++ b/library/src/schemas/looseObject/looseObject.test-d.ts @@ -54,8 +54,8 @@ describe('looseObject', () => { expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; } & { [key: string]: unknown } @@ -67,7 +67,7 @@ describe('looseObject', () => { { key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; } & { [key: string]: unknown } diff --git a/library/src/schemas/looseObject/looseObject.test.ts b/library/src/schemas/looseObject/looseObject.test.ts index 988024d84..13dffdd2c 100644 --- a/library/src/schemas/looseObject/looseObject.test.ts +++ b/library/src/schemas/looseObject/looseObject.test.ts @@ -134,6 +134,7 @@ describe('looseObject', () => { test('for optional entry', () => { expectNoSchemaIssue(looseObject({ key: optional(string()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: 'foo' }, ]); @@ -142,6 +143,7 @@ describe('looseObject', () => { test('for nullish entry', () => { expectNoSchemaIssue(looseObject({ key: nullish(number()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: null }, { key: 123 }, diff --git a/library/src/schemas/looseObject/looseObjectAsync.test-d.ts b/library/src/schemas/looseObject/looseObjectAsync.test-d.ts index 43657de04..3f8e89efd 100644 --- a/library/src/schemas/looseObject/looseObjectAsync.test-d.ts +++ b/library/src/schemas/looseObject/looseObjectAsync.test-d.ts @@ -59,8 +59,8 @@ describe('looseObjectAsync', () => { expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; } & { [key: string]: unknown } @@ -72,7 +72,7 @@ describe('looseObjectAsync', () => { { key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; } & { [key: string]: unknown } diff --git a/library/src/schemas/looseObject/looseObjectAsync.test.ts b/library/src/schemas/looseObject/looseObjectAsync.test.ts index 0528a37be..c21648b12 100644 --- a/library/src/schemas/looseObject/looseObjectAsync.test.ts +++ b/library/src/schemas/looseObject/looseObjectAsync.test.ts @@ -153,14 +153,25 @@ describe('looseObjectAsync', () => { test('for optional entry', async () => { await expectNoSchemaIssueAsync( looseObjectAsync({ key: optional(string()) }), - [{}, { key: undefined }, { key: 'foo' }] + [ + {}, + // @ts-expect-error + { key: undefined }, + { key: 'foo' }, + ] ); }); test('for nullish entry', async () => { await expectNoSchemaIssueAsync( looseObjectAsync({ key: nullish(number()) }), - [{}, { key: undefined }, { key: null }, { key: 123 }] + [ + {}, + // @ts-expect-error + { key: undefined }, + { key: null }, + { key: 123 }, + ] ); }); diff --git a/library/src/schemas/object/object.test-d.ts b/library/src/schemas/object/object.test-d.ts index b8f0326c0..cc60f792a 100644 --- a/library/src/schemas/object/object.test-d.ts +++ b/library/src/schemas/object/object.test-d.ts @@ -52,8 +52,8 @@ describe('object', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; }>(); @@ -63,7 +63,7 @@ describe('object', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; }>(); diff --git a/library/src/schemas/object/object.test.ts b/library/src/schemas/object/object.test.ts index 40dc363c1..7a224db2a 100644 --- a/library/src/schemas/object/object.test.ts +++ b/library/src/schemas/object/object.test.ts @@ -139,6 +139,7 @@ describe('object', () => { test('for optional entry', () => { expectNoSchemaIssue(object({ key: optional(string()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: 'foo' }, ]); @@ -147,6 +148,7 @@ describe('object', () => { test('for nullish entry', () => { expectNoSchemaIssue(object({ key: nullish(number()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: null }, { key: 123 }, diff --git a/library/src/schemas/object/objectAsync.test-d.ts b/library/src/schemas/object/objectAsync.test-d.ts index 332704051..429ef7c8e 100644 --- a/library/src/schemas/object/objectAsync.test-d.ts +++ b/library/src/schemas/object/objectAsync.test-d.ts @@ -52,8 +52,8 @@ describe('objectAsync', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; }>(); @@ -63,7 +63,7 @@ describe('objectAsync', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; }>(); diff --git a/library/src/schemas/object/objectAsync.test.ts b/library/src/schemas/object/objectAsync.test.ts index ceb5ec772..70978928e 100644 --- a/library/src/schemas/object/objectAsync.test.ts +++ b/library/src/schemas/object/objectAsync.test.ts @@ -151,6 +151,7 @@ describe('objectAsync', () => { test('for optional entry', async () => { await expectNoSchemaIssueAsync(objectAsync({ key: optional(string()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: 'foo' }, ]); @@ -159,6 +160,7 @@ describe('objectAsync', () => { test('for nullish entry', async () => { await expectNoSchemaIssueAsync(objectAsync({ key: nullish(number()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: null }, { key: 123 }, diff --git a/library/src/schemas/objectWithRest/objectWithRest.test-d.ts b/library/src/schemas/objectWithRest/objectWithRest.test-d.ts index e43200bfe..aa858fff3 100644 --- a/library/src/schemas/objectWithRest/objectWithRest.test-d.ts +++ b/library/src/schemas/objectWithRest/objectWithRest.test-d.ts @@ -64,8 +64,8 @@ describe('objectWithRest', () => { expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; } & { [key: string]: boolean } @@ -77,7 +77,7 @@ describe('objectWithRest', () => { { key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; } & { [key: string]: boolean } diff --git a/library/src/schemas/objectWithRest/objectWithRestAsync.test-d.ts b/library/src/schemas/objectWithRest/objectWithRestAsync.test-d.ts index 0419c8e56..d564d8263 100644 --- a/library/src/schemas/objectWithRest/objectWithRestAsync.test-d.ts +++ b/library/src/schemas/objectWithRest/objectWithRestAsync.test-d.ts @@ -67,8 +67,8 @@ describe('objectWithRestAsync', () => { expectTypeOf>().toEqualTypeOf< { key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; } & { [key: string]: boolean } @@ -80,7 +80,7 @@ describe('objectWithRestAsync', () => { { key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; } & { [key: string]: boolean } diff --git a/library/src/schemas/strictObject/strictObject.test-d.ts b/library/src/schemas/strictObject/strictObject.test-d.ts index a4252796c..72c9a531a 100644 --- a/library/src/schemas/strictObject/strictObject.test-d.ts +++ b/library/src/schemas/strictObject/strictObject.test-d.ts @@ -53,8 +53,8 @@ describe('strictObject', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; }>(); @@ -64,7 +64,7 @@ describe('strictObject', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; }>(); diff --git a/library/src/schemas/strictObject/strictObject.test.ts b/library/src/schemas/strictObject/strictObject.test.ts index 2bd22e917..138736119 100644 --- a/library/src/schemas/strictObject/strictObject.test.ts +++ b/library/src/schemas/strictObject/strictObject.test.ts @@ -128,6 +128,7 @@ describe('strictObject', () => { test('for optional entry', () => { expectNoSchemaIssue(strictObject({ key: optional(string()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: 'foo' }, ]); @@ -136,6 +137,7 @@ describe('strictObject', () => { test('for nullish entry', () => { expectNoSchemaIssue(strictObject({ key: nullish(number()) }), [ {}, + // @ts-expect-error { key: undefined }, { key: null }, { key: 123 }, diff --git a/library/src/schemas/strictObject/strictObjectAsync.test-d.ts b/library/src/schemas/strictObject/strictObjectAsync.test-d.ts index cb1c376c4..b1ef8c582 100644 --- a/library/src/schemas/strictObject/strictObjectAsync.test-d.ts +++ b/library/src/schemas/strictObject/strictObjectAsync.test-d.ts @@ -58,8 +58,8 @@ describe('strictObjectAsync', () => { test('of input', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; - key2?: string | undefined; - key3?: string | null | undefined; + key2?: string; + key3?: string | null; key4: { key: number }; key5: string; }>(); @@ -69,7 +69,7 @@ describe('strictObjectAsync', () => { expectTypeOf>().toEqualTypeOf<{ key1: string; key2: string; - key3?: string | null | undefined; + key3?: string | null; key4: { key: number }; readonly key5: string; }>(); diff --git a/library/src/schemas/strictObject/strictObjectAsync.test.ts b/library/src/schemas/strictObject/strictObjectAsync.test.ts index 2c70e1bca..4328ad92d 100644 --- a/library/src/schemas/strictObject/strictObjectAsync.test.ts +++ b/library/src/schemas/strictObject/strictObjectAsync.test.ts @@ -146,14 +146,25 @@ describe('strictObjectAsync', () => { test('for optional entry', async () => { await expectNoSchemaIssueAsync( strictObjectAsync({ key: optional(string()) }), - [{}, { key: undefined }, { key: 'foo' }] + [ + {}, + // @ts-expect-error + { key: undefined }, + { key: 'foo' }, + ] ); }); test('for nullish entry', async () => { await expectNoSchemaIssueAsync( strictObjectAsync({ key: nullish(number()) }), - [{}, { key: undefined }, { key: null }, { key: 123 }] + [ + {}, + // @ts-expect-error + { key: undefined }, + { key: null }, + { key: 123 }, + ] ); }); }); diff --git a/library/src/types/config.ts b/library/src/types/config.ts index f06cd2791..332ee7707 100644 --- a/library/src/types/config.ts +++ b/library/src/types/config.ts @@ -8,17 +8,17 @@ export interface Config> { /** * The selected language. */ - readonly lang?: string; + readonly lang?: string | undefined; /** * The error message. */ - readonly message?: ErrorMessage; + readonly message?: ErrorMessage | undefined; /** * Whether it was abort early. */ - readonly abortEarly?: boolean; + readonly abortEarly?: boolean | undefined; /** * Whether the pipe was abort early. */ - readonly abortPipeEarly?: boolean; + readonly abortPipeEarly?: boolean | undefined; } diff --git a/library/src/types/issue.ts b/library/src/types/issue.ts index b56c7fee0..d11f95401 100644 --- a/library/src/types/issue.ts +++ b/library/src/types/issue.ts @@ -241,18 +241,18 @@ export interface BaseIssue extends Config> { /** * The input requirement. */ - readonly requirement?: unknown; + readonly requirement?: unknown | undefined; /** * The issue path. * * TODO: Investigate if it is possible to make the path type safe based on the * input. */ - readonly path?: [IssuePathItem, ...IssuePathItem[]]; + readonly path?: [IssuePathItem, ...IssuePathItem[]] | undefined; /** * The sub issues. */ - readonly issues?: [BaseIssue, ...BaseIssue[]]; + readonly issues?: [BaseIssue, ...BaseIssue[]] | undefined; } /** diff --git a/library/src/types/object.ts b/library/src/types/object.ts index e66eb8f7c..06ed84788 100644 --- a/library/src/types/object.ts +++ b/library/src/types/object.ts @@ -79,20 +79,6 @@ export type ObjectKeys< >, > = MaybeReadonly<[keyof TSchema['entries'], ...(keyof TSchema['entries'])[]]>; -/** - * Infer entries input type. - */ -type InferEntriesInput = { - -readonly [TKey in keyof TEntries]: InferInput; -}; - -/** - * Infer entries output type. - */ -type InferEntriesOutput = { - -readonly [TKey in keyof TEntries]: InferOutput; -}; - /** * Question mark schema type. * @@ -114,6 +100,67 @@ type QuestionMarkSchema = unknown >; +/** + * Has default type. + */ +type HasDefault = [ + TSchema['default'], +] extends [never] + ? false + : true; + +/** + * Exact optional input type. + */ +type ExactOptionalInput< + TSchema extends + | BaseSchema> + | BaseSchemaAsync>, +> = TSchema extends + | NullishSchema + | NullishSchemaAsync + ? ExactOptionalInput | null + : TSchema extends + | OptionalSchema + | OptionalSchemaAsync + ? ExactOptionalInput + : InferInput; + +/** + * Exact optional output type. + */ +type ExactOptionalOutput< + TSchema extends + | BaseSchema> + | BaseSchemaAsync>, +> = TSchema extends + | NullishSchema + | NullishSchemaAsync + ? HasDefault extends true + ? InferOutput + : ExactOptionalOutput | null + : TSchema extends + | OptionalSchema + | OptionalSchemaAsync + ? HasDefault extends true + ? InferOutput + : ExactOptionalOutput + : InferOutput; + +/** + * Infer entries input type. + */ +type InferEntriesInput = { + -readonly [TKey in keyof TEntries]: ExactOptionalInput; +}; + +/** + * Infer entries output type. + */ +type InferEntriesOutput = { + -readonly [TKey in keyof TEntries]: ExactOptionalOutput; +}; + /** * Optional input keys type. */ @@ -128,9 +175,9 @@ type OptionalInputKeys = { */ type OptionalOutputKeys = { [TKey in keyof TEntries]: TEntries[TKey] extends QuestionMarkSchema - ? [TEntries[TKey]['default']] extends [never] - ? TKey - : never + ? HasDefault extends true + ? never + : TKey : never; }[keyof TEntries]; diff --git a/library/src/utils/_addIssue/_addIssue.ts b/library/src/utils/_addIssue/_addIssue.ts index 155435456..f5fc8ceb7 100644 --- a/library/src/utils/_addIssue/_addIssue.ts +++ b/library/src/utils/_addIssue/_addIssue.ts @@ -39,15 +39,14 @@ type Context = * Other type. */ interface Other { - input?: unknown; - expected?: string; - received?: string; - message?: ErrorMessage>; - path?: [IssuePathItem, ...IssuePathItem[]]; - issues?: [ - BaseIssue>, - ...BaseIssue>[], - ]; + input?: unknown | undefined; + expected?: string | undefined; + received?: string | undefined; + message?: ErrorMessage> | undefined; + path?: [IssuePathItem, ...IssuePathItem[]] | undefined; + issues?: + | [BaseIssue>, ...BaseIssue>[]] + | undefined; } /** diff --git a/library/tsconfig.json b/library/tsconfig.json index 0470dfcb3..9cbe94e07 100644 --- a/library/tsconfig.json +++ b/library/tsconfig.json @@ -1,15 +1,16 @@ { "compilerOptions": { - "strict": true, + "allowImportingTsExtensions": true, + "declaration": true, + "exactOptionalPropertyTypes": true, + "isolatedDeclarations": true, "lib": ["ESNext", "DOM"], - "target": "ESNext", "module": "ESNext", "moduleResolution": "node", - "allowImportingTsExtensions": true, - "skipLibCheck": true, "noEmit": true, - "declaration": true, - "isolatedDeclarations": true + "skipLibCheck": true, + "strict": true, + "target": "ESNext" }, "include": ["src"] }