From 16e27384a51d946b604b1c3fac84ce025ed9897e Mon Sep 17 00:00:00 2001 From: Lexus Drumgold Date: Tue, 23 May 2023 20:23:20 -0400 Subject: [PATCH] feat(types): `IsRequiredKey`, `IfRequiredKey` Signed-off-by: Lexus Drumgold --- src/types/__tests__/if-key-required.spec-d.ts | 28 +++++++++++++++++++ src/types/__tests__/is-key-required.spec-d.ts | 25 +++++++++++++++++ src/types/if-key-required.ts | 27 ++++++++++++++++++ src/types/index.ts | 2 ++ src/types/is-key-required.ts | 19 +++++++++++++ src/types/keys-required.ts | 8 ++---- 6 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 src/types/__tests__/if-key-required.spec-d.ts create mode 100644 src/types/__tests__/is-key-required.spec-d.ts create mode 100644 src/types/if-key-required.ts create mode 100644 src/types/is-key-required.ts diff --git a/src/types/__tests__/if-key-required.spec-d.ts b/src/types/__tests__/if-key-required.spec-d.ts new file mode 100644 index 00000000..e5057223 --- /dev/null +++ b/src/types/__tests__/if-key-required.spec-d.ts @@ -0,0 +1,28 @@ +/** + * @file Type Tests - IfRequiredKey + * @module tutils/types/tests/unit-d/IfRequiredKey + */ + +import type Author from '#fixtures/author.interface' +import type TestSubject from '../if-key-required' + +describe('unit-d:types/IfRequiredKey', () => { + type False = false + type True = true + + it('should equal False if IsRequiredKey extends false', () => { + // Arrange + type K = 'email' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal True if IsRequiredKey extends true', () => { + // Arrange + type K = 'first_name' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) +}) diff --git a/src/types/__tests__/is-key-required.spec-d.ts b/src/types/__tests__/is-key-required.spec-d.ts new file mode 100644 index 00000000..cced57a2 --- /dev/null +++ b/src/types/__tests__/is-key-required.spec-d.ts @@ -0,0 +1,25 @@ +/** + * @file Type Tests - IsRequiredKey + * @module tutils/types/tests/unit-d/IsRequiredKey + */ + +import type Author from '#fixtures/author.interface' +import type TestSubject from '../is-key-required' + +describe('unit-d:types/IsRequiredKey', () => { + it('should equal false if K is not required property of T', () => { + // Arrange + type K = 'email' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required property of T', () => { + // Arrange + type K = 'last_name' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) +}) diff --git a/src/types/if-key-required.ts b/src/types/if-key-required.ts new file mode 100644 index 00000000..d45390d6 --- /dev/null +++ b/src/types/if-key-required.ts @@ -0,0 +1,27 @@ +/** + * @file Type Definitions - IfRequiredKey + * @module tutils/types/IfRequiredKey + */ + +import type IndexSignature from './index-signature' +import type IsRequiredKey from './is-key-required' + +/** + * Conditional type that resolves depending on whether or not `K` is a required + * property of `T`. + * + * @see {@linkcode IsRequiredKey} + * + * @template T - Type to evaluate + * @template K - Key to evaluate + * @template True - Type if `K` is required property + * @template False - Type if `K` is not required property + */ +type IfRequiredKey = IsRequiredKey< + T, + K +> extends true + ? True + : False + +export type { IfRequiredKey as default } diff --git a/src/types/index.ts b/src/types/index.ts index 11b88c8a..8db5503e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -35,6 +35,7 @@ export type { default as IfFunction } from './if-function' export type { default as IfJsonPrimitive } from './if-json-primitive' export type { default as IfOptionalKey } from './if-key-optional' export type { default as IfExactOptionalKey } from './if-key-optional-exact' +export type { default as IfRequiredKey } from './if-key-required' export type { default as IfNever } from './if-never' export type { default as IfNil } from './if-nil' export type { default as IfNull } from './if-null' @@ -58,6 +59,7 @@ export type { default as IsFunction } from './is-function' export type { default as IsJsonPrimitive } from './is-json-primitive' export type { default as IsOptionalKey } from './is-key-optional' export type { default as IsExactOptionalKey } from './is-key-optional-exact' +export type { default as IsRequiredKey } from './is-key-required' export type { default as IsNever } from './is-never' export type { default as IsNil } from './is-nil' export type { default as IsNull } from './is-null' diff --git a/src/types/is-key-required.ts b/src/types/is-key-required.ts new file mode 100644 index 00000000..2291c0df --- /dev/null +++ b/src/types/is-key-required.ts @@ -0,0 +1,19 @@ +/** + * @file Type Definitions - IsRequiredKey + * @module tutils/types/IsRequiredKey + */ + +import type IndexSignature from './index-signature' +import type RequiredKeys from './keys-required' + +/** + * Returns a boolean indicating if `K` is a required property of `T`. + * + * @template T - Type to evaluate + * @template K - Key to evaluate + */ +type IsRequiredKey = K extends RequiredKeys + ? true + : false + +export type { IsRequiredKey as default } diff --git a/src/types/keys-required.ts b/src/types/keys-required.ts index 3a97cb30..2ddfbfe4 100644 --- a/src/types/keys-required.ts +++ b/src/types/keys-required.ts @@ -3,16 +3,12 @@ * @module tutils/types/RequiredKeys */ -import type ObjectPlain from './object-plain' - /** * Extracts all required keys from `T`. * - * @todo support nested keys - * - * @template T - Object type to evaluate + * @template T - Type to evaluate */ -type RequiredKeys = Exclude< +type RequiredKeys = Exclude< { [K in keyof T]: T extends Record ? K : never }[keyof T], undefined >