From 737c09f6b18a07ce8c5097a447dad268f956a9b3 Mon Sep 17 00:00:00 2001 From: Lexus Drumgold Date: Sun, 2 Jul 2023 20:48:59 -0400 Subject: [PATCH] refactor(types): improve type implementations and tests - dropped EnsureString, Keys, MergeDefaults, OmitNative, Omit, PickNative, Pick - MergeDefaults functionality was merged into Merge - TupleLength -> Length - TupleToUnion -> ArrayToUnion - Omit and Pick were improved, but still under review - added several new types Signed-off-by: Lexus Drumgold --- .attw.json | 7 + .codecov.yml | 4 +- .dictionary.txt | 5 + .eslintrc.base.cjs | 4 +- .eslintrc.cjs | 28 +- .github/dependabot.yml | 2 + .github/infrastructure.yml | 2 +- .github/workflows/ci.yml | 17 +- .vscode/settings.json | 1 + __fixtures__/book.interface.ts | 3 +- __fixtures__/ordered-pair.ts | 26 - __fixtures__/vehicle.ts | 31 + __tests__/reporters/notifier.ts | 68 +- __tests__/ts/v4/tsconfig.typecheck.json | 17 +- package.json | 49 +- patches/vitest+0.32.2.dev.patch | 10 + src/types/__tests__/array-to-union.spec-d.ts | 42 + src/types/__tests__/assign.spec-d.ts | 169 ++- src/types/__tests__/at.spec-d.ts | 346 ++++- src/types/__tests__/booleanish.spec-d.ts | 8 + src/types/__tests__/built-in.spec-d.ts | 162 +- src/types/__tests__/class-abstract.spec-d.ts | 23 +- src/types/__tests__/class.spec-d.ts | 18 +- .../__tests__/constructor-abstract.spec-d.ts | 23 +- src/types/__tests__/constructor.spec-d.ts | 12 +- .../__tests__/continuous-value.spec-d.ts | 4 +- src/types/__tests__/dot.spec-d.ts | 12 + src/types/__tests__/empty-array.spec-d.ts | 10 +- src/types/__tests__/empty-object.spec-d.ts | 10 +- src/types/__tests__/ensure-string.spec-d.ts | 12 - src/types/__tests__/fallback.spec-d.ts | 24 +- src/types/__tests__/get.spec-d.ts | 469 +++++- src/types/__tests__/has-keys.spec-d.ts | 36 + src/types/__tests__/head.spec-d.ts | 79 +- src/types/__tests__/if-any-or-never.spec-d.ts | 15 +- src/types/__tests__/if-any.spec-d.ts | 12 +- src/types/__tests__/if-array.spec-d.ts | 27 +- src/types/__tests__/if-big-int.spec-d.ts | 27 +- src/types/__tests__/if-boolean.spec-d.ts | 27 +- src/types/__tests__/if-equal.spec-d.ts | 18 +- src/types/__tests__/if-false.spec-d.ts | 33 + src/types/__tests__/if-function.spec-d.ts | 27 +- .../__tests__/if-index-signature.spec-d.ts | 54 + .../__tests__/if-integer-negative.spec-d.ts | 33 + src/types/__tests__/if-integer.spec-d.ts | 33 + .../__tests__/if-json-primitive.spec-d.ts | 31 +- .../__tests__/if-key-optional-exact.spec-d.ts | 44 +- src/types/__tests__/if-key-optional.spec-d.ts | 44 +- src/types/__tests__/if-key-readonly.spec-d.ts | 52 + src/types/__tests__/if-key-required.spec-d.ts | 44 +- src/types/__tests__/if-keys.spec-d.ts | 31 + src/types/__tests__/if-literal.spec-d.ts | 34 + src/types/__tests__/if-negative.spec-d.ts | 33 + src/types/__tests__/if-never.spec-d.ts | 8 +- src/types/__tests__/if-nil.spec-d.ts | 27 +- src/types/__tests__/if-null.spec-d.ts | 27 +- src/types/__tests__/if-number.spec-d.ts | 27 +- .../__tests__/if-numeric-negative.spec-d.ts | 33 + src/types/__tests__/if-numeric.spec-d.ts | 27 +- src/types/__tests__/if-object-curly.spec-d.ts | 27 +- src/types/__tests__/if-object-plain.spec-d.ts | 31 +- src/types/__tests__/if-object.spec-d.ts | 27 +- src/types/__tests__/if-primitive.spec-d.ts | 31 +- src/types/__tests__/if-string.spec-d.ts | 27 +- src/types/__tests__/if-symbol.spec-d.ts | 27 +- src/types/__tests__/if-true.spec-d.ts | 33 + src/types/__tests__/if-tuple.spec-d.ts | 30 +- src/types/__tests__/if-undefined.spec-d.ts | 27 +- src/types/__tests__/if-unknown.spec-d.ts | 12 +- src/types/__tests__/indices.spec-d.ts | 72 +- src/types/__tests__/intersection.spec-d.ts | 23 + src/types/__tests__/is-array.spec-d.ts | 20 +- src/types/__tests__/is-big-int.spec-d.ts | 17 +- src/types/__tests__/is-boolean.spec-d.ts | 14 +- src/types/__tests__/is-equal.spec-d.ts | 9 +- src/types/__tests__/is-false.spec-d.ts | 33 + src/types/__tests__/is-function.spec-d.ts | 17 +- .../__tests__/is-index-signature.spec-d.ts | 195 +++ .../__tests__/is-integer-negative.spec-d.ts | 31 + src/types/__tests__/is-integer.spec-d.ts | 34 + .../__tests__/is-json-primitive.spec-d.ts | 25 +- .../__tests__/is-key-optional-exact.spec-d.ts | 429 +++++- src/types/__tests__/is-key-optional.spec-d.ts | 451 +++++- src/types/__tests__/is-key-readonly.spec-d.ts | 571 +++++++ src/types/__tests__/is-key-required.spec-d.ts | 448 +++++- src/types/__tests__/is-literal.spec-d.ts | 143 ++ src/types/__tests__/is-negative.spec-d.ts | 38 + src/types/__tests__/is-nil.spec-d.ts | 17 +- src/types/__tests__/is-null.spec-d.ts | 12 +- src/types/__tests__/is-number.spec-d.ts | 19 +- .../__tests__/is-numeric-negative.spec-d.ts | 41 + src/types/__tests__/is-numeric.spec-d.ts | 21 +- src/types/__tests__/is-object-curly.spec-d.ts | 28 +- src/types/__tests__/is-object-plain.spec-d.ts | 25 +- src/types/__tests__/is-object.spec-d.ts | 21 +- src/types/__tests__/is-primitive.spec-d.ts | 24 +- src/types/__tests__/is-string.spec-d.ts | 13 +- src/types/__tests__/is-symbol.spec-d.ts | 15 +- src/types/__tests__/is-true.spec-d.ts | 33 + src/types/__tests__/is-tuple.spec-d.ts | 24 +- src/types/__tests__/is-undefined.spec-d.ts | 14 +- src/types/__tests__/join.spec-d.ts | 84 +- src/types/__tests__/json-array.spec-d.ts | 8 +- src/types/__tests__/json-object.spec-d.ts | 12 +- src/types/__tests__/json-primitive.spec-d.ts | 4 +- src/types/__tests__/json-value.spec-d.ts | 10 +- .../__tests__/jsonifiable-instance.spec-d.ts | 2 +- .../__tests__/jsonifiable-object.spec-d.ts | 14 +- src/types/__tests__/jsonifiable.spec-d.ts | 16 +- src/types/__tests__/keyof.spec-d.ts | 222 +++ .../__tests__/keys-optional-exact.spec-d.ts | 150 ++ src/types/__tests__/keys-optional.spec-d.ts | 163 +- src/types/__tests__/keys-readonly.spec-d.ts | 150 ++ src/types/__tests__/keys-required.spec-d.ts | 180 ++- src/types/__tests__/keys.spec-d.ts | 90 -- src/types/__tests__/length.spec-d.ts | 45 + src/types/__tests__/literal-union.spec-d.ts | 4 +- src/types/__tests__/merge-defaults.spec-d.ts | 40 - src/types/__tests__/merge.spec-d.ts | 299 +++- src/types/__tests__/number-like.spec-d.ts | 4 +- src/types/__tests__/number-string.spec-d.ts | 4 +- .../__tests__/numeric-negative.spec-d.ts | 13 + src/types/__tests__/numeric.spec-d.ts | 10 +- .../__tests__/omit-index-signature.spec-d.ts | 40 + src/types/__tests__/omit-native.spec-d.ts | 17 - src/types/__tests__/omit.spec-d.ts | 34 - src/types/__tests__/one-or-many.spec-d.ts | 13 +- src/types/__tests__/or-lowercase.spec-d.ts | 8 +- src/types/__tests__/or-uppercase.spec-d.ts | 8 +- src/types/__tests__/overwrite.spec-d.ts | 111 +- src/types/__tests__/path.spec-d.ts | 441 +++++- src/types/__tests__/pick-native.spec-d.ts | 17 - src/types/__tests__/pick.spec-d.ts | 42 - src/types/__tests__/primitive.spec-d.ts | 10 +- src/types/__tests__/promisable.spec-d.ts | 6 +- src/types/__tests__/property-key.spec-d.ts | 6 +- src/types/__tests__/range-natural.spec-d.ts | 18 +- src/types/__tests__/reverse.spec-d.ts | 115 ++ src/types/__tests__/sift.spec-d.ts | 47 +- src/types/__tests__/simplify.spec-d.ts | 18 +- src/types/__tests__/split.spec-d.ts | 177 ++- src/types/__tests__/stringafiable.spec-d.ts | 2 +- src/types/__tests__/subtract.spec-d.ts | 13 + src/types/__tests__/tail.spec-d.ts | 76 +- src/types/__tests__/template-data.spec-d.ts | 10 + src/types/__tests__/timestamp.spec-d.ts | 4 +- src/types/__tests__/trim-end.spec-d.ts | 20 +- src/types/__tests__/trim-start.spec-d.ts | 20 +- src/types/__tests__/trim.spec-d.ts | 20 +- src/types/__tests__/tuple-length.spec-d.ts | 20 - src/types/__tests__/tuple-to-union.spec-d.ts | 16 - .../__tests__/union-to-intersection.spec-d.ts | 21 +- src/types/__tests__/union-to-tuple.spec-d.ts | 6 +- src/types/__tests__/unwrap-numeric.spec-d.ts | 49 + src/types/array-to-union.ts | 35 + src/types/assign.ts | 76 +- src/types/at.ts | 230 ++- src/types/booleanish.ts | 2 +- src/types/built-in.ts | 5 +- src/types/class-abstract.ts | 4 +- src/types/class.ts | 4 +- src/types/constructor-abstract.ts | 2 +- src/types/constructor.ts | 2 +- src/types/dot.ts | 11 + src/types/empty-array.ts | 2 +- src/types/empty-object.ts | 11 +- src/types/ensure-string.ts | 15 - src/types/exact-optional-property-types.ts | 9 +- src/types/fallback.ts | 26 +- src/types/get.ts | 122 +- src/types/has-keys.ts | 45 + src/types/head.ts | 44 +- src/types/if-any-or-never.ts | 23 +- src/types/if-any.ts | 23 +- src/types/if-array.ts | 43 +- src/types/if-big-int.ts | 37 +- src/types/if-boolean.ts | 40 +- src/types/if-equal.ts | 16 +- src/types/if-false.ts | 46 + src/types/if-function.ts | 40 +- src/types/if-index-signature.ts | 38 + src/types/if-integer-negative.ts | 49 + src/types/if-integer.ts | 49 + src/types/if-json-primitive.ts | 45 +- src/types/if-key-optional-exact.ts | 33 +- src/types/if-key-optional.ts | 32 +- src/types/if-key-readonly.ts | 38 + src/types/if-key-required.ts | 32 +- src/types/if-keys.ts | 49 + src/types/if-literal.ts | 78 + src/types/if-negative.ts | 64 + src/types/if-never.ts | 23 +- src/types/if-nil.ts | 40 +- src/types/if-null.ts | 34 +- src/types/if-number.ts | 37 +- src/types/if-numeric-negative.ts | 55 + src/types/if-numeric.ts | 46 +- src/types/if-object-curly.ts | 42 +- src/types/if-object-plain.ts | 19 +- src/types/if-object.ts | 40 +- src/types/if-primitive.ts | 52 +- src/types/if-string.ts | 34 +- src/types/if-symbol.ts | 37 +- src/types/if-true.ts | 46 + src/types/if-tuple.ts | 37 +- src/types/if-undefined.ts | 34 +- src/types/if-unknown.ts | 23 +- src/types/index.ts | 39 +- src/types/indices.ts | 66 +- src/types/intersection.ts | 27 + src/types/is-any-or-never.ts | 16 + src/types/is-any.ts | 13 + src/types/is-array.ts | 35 +- src/types/is-big-int.ts | 26 +- src/types/is-boolean.ts | 26 +- src/types/is-equal.ts | 28 +- src/types/is-false.ts | 34 + src/types/is-function.ts | 36 +- src/types/is-index-signature.ts | 53 + src/types/is-integer-negative.ts | 49 + src/types/is-integer.ts | 61 + src/types/is-json-primitive.ts | 38 +- src/types/is-key-optional-exact.ts | 49 +- src/types/is-key-optional.ts | 39 +- src/types/is-key-readonly.ts | 49 + src/types/is-key-required.ts | 45 +- src/types/is-literal.ts | 98 ++ src/types/is-negative.ts | 73 + src/types/is-never.ts | 13 + src/types/is-nil.ts | 26 +- src/types/is-null.ts | 20 +- src/types/is-number.ts | 26 +- src/types/is-numeric-negative.ts | 51 + src/types/is-numeric.ts | 41 +- src/types/is-object-curly.ts | 26 +- src/types/is-object-plain.ts | 30 +- src/types/is-object.ts | 29 +- src/types/is-primitive.ts | 30 +- src/types/is-string.ts | 23 +- src/types/is-symbol.ts | 20 +- src/types/is-true.ts | 34 + src/types/is-tuple.ts | 31 +- src/types/is-undefined.ts | 24 +- src/types/is-unknown.ts | 13 + src/types/join.ts | 44 +- src/types/keyof.ts | 51 + src/types/keys-optional-exact.ts | 61 + src/types/keys-optional.ts | 85 +- src/types/keys-readonly.ts | 82 + src/types/keys-required.ts | 120 +- src/types/keys.ts | 53 - src/types/length.ts | 48 + src/types/merge-defaults.ts | 54 - src/types/merge.ts | 131 +- src/types/nilable.ts | 8 +- src/types/nullable.ts | 8 +- src/types/number-like.ts | 8 +- src/types/numeric-negative.ts | 17 + src/types/numeric.ts | 10 +- src/types/object-curly.ts | 9 +- src/types/omit-index-signature.ts | 30 + src/types/omit-native.ts | 16 - src/types/omit.ts | 53 - src/types/one-or-many.ts | 6 +- src/types/opaque.ts | 2 + src/types/optional.ts | 4 + src/types/or-lowercase.ts | 11 +- src/types/or-uppercase.ts | 11 +- src/types/overwrite.ts | 73 +- src/types/partial-native.ts | 17 + src/types/partial.ts | 40 + src/types/path.ts | 154 +- src/types/pick-native.ts | 16 - src/types/pick.ts | 84 -- src/types/promisable.ts | 4 + src/types/range-natural.ts | 33 +- src/types/reverse.ts | 86 ++ src/types/sift.ts | 37 +- src/types/simplify.ts | 2 +- src/types/split.ts | 64 +- src/types/stringify.ts | 25 + src/types/subtract.ts | 50 + src/types/tail.ts | 39 +- src/types/template-data.ts | 19 + src/types/trim-end.ts | 19 +- src/types/trim-start.ts | 19 +- src/types/trim.ts | 17 +- src/types/tuple-length.ts | 17 - src/types/tuple-to-union.ts | 16 - src/types/union-to-intersection.ts | 29 +- src/types/union-to-tuple.ts | 29 +- src/types/unwrap-numeric.ts | 54 + src/utils/__tests__/is-booleanish.spec.ts | 10 +- src/utils/is-booleanish.ts | 4 +- vitest.config.ts | 13 +- yarn.lock | 1328 +++++++++++++---- 296 files changed, 12664 insertions(+), 2491 deletions(-) create mode 100644 .attw.json delete mode 100644 __fixtures__/ordered-pair.ts create mode 100644 __fixtures__/vehicle.ts create mode 100644 patches/vitest+0.32.2.dev.patch create mode 100644 src/types/__tests__/array-to-union.spec-d.ts create mode 100644 src/types/__tests__/dot.spec-d.ts delete mode 100644 src/types/__tests__/ensure-string.spec-d.ts create mode 100644 src/types/__tests__/has-keys.spec-d.ts create mode 100644 src/types/__tests__/if-false.spec-d.ts create mode 100644 src/types/__tests__/if-index-signature.spec-d.ts create mode 100644 src/types/__tests__/if-integer-negative.spec-d.ts create mode 100644 src/types/__tests__/if-integer.spec-d.ts create mode 100644 src/types/__tests__/if-key-readonly.spec-d.ts create mode 100644 src/types/__tests__/if-keys.spec-d.ts create mode 100644 src/types/__tests__/if-literal.spec-d.ts create mode 100644 src/types/__tests__/if-negative.spec-d.ts create mode 100644 src/types/__tests__/if-numeric-negative.spec-d.ts create mode 100644 src/types/__tests__/if-true.spec-d.ts create mode 100644 src/types/__tests__/intersection.spec-d.ts create mode 100644 src/types/__tests__/is-false.spec-d.ts create mode 100644 src/types/__tests__/is-index-signature.spec-d.ts create mode 100644 src/types/__tests__/is-integer-negative.spec-d.ts create mode 100644 src/types/__tests__/is-integer.spec-d.ts create mode 100644 src/types/__tests__/is-key-readonly.spec-d.ts create mode 100644 src/types/__tests__/is-literal.spec-d.ts create mode 100644 src/types/__tests__/is-negative.spec-d.ts create mode 100644 src/types/__tests__/is-numeric-negative.spec-d.ts create mode 100644 src/types/__tests__/is-true.spec-d.ts create mode 100644 src/types/__tests__/keyof.spec-d.ts create mode 100644 src/types/__tests__/keys-optional-exact.spec-d.ts create mode 100644 src/types/__tests__/keys-readonly.spec-d.ts delete mode 100644 src/types/__tests__/keys.spec-d.ts create mode 100644 src/types/__tests__/length.spec-d.ts delete mode 100644 src/types/__tests__/merge-defaults.spec-d.ts create mode 100644 src/types/__tests__/numeric-negative.spec-d.ts create mode 100644 src/types/__tests__/omit-index-signature.spec-d.ts delete mode 100644 src/types/__tests__/omit-native.spec-d.ts delete mode 100644 src/types/__tests__/omit.spec-d.ts delete mode 100644 src/types/__tests__/pick-native.spec-d.ts delete mode 100644 src/types/__tests__/pick.spec-d.ts create mode 100644 src/types/__tests__/reverse.spec-d.ts create mode 100644 src/types/__tests__/subtract.spec-d.ts create mode 100644 src/types/__tests__/template-data.spec-d.ts delete mode 100644 src/types/__tests__/tuple-length.spec-d.ts delete mode 100644 src/types/__tests__/tuple-to-union.spec-d.ts create mode 100644 src/types/__tests__/unwrap-numeric.spec-d.ts create mode 100644 src/types/array-to-union.ts create mode 100644 src/types/dot.ts delete mode 100644 src/types/ensure-string.ts create mode 100644 src/types/has-keys.ts create mode 100644 src/types/if-false.ts create mode 100644 src/types/if-index-signature.ts create mode 100644 src/types/if-integer-negative.ts create mode 100644 src/types/if-integer.ts create mode 100644 src/types/if-key-readonly.ts create mode 100644 src/types/if-keys.ts create mode 100644 src/types/if-literal.ts create mode 100644 src/types/if-negative.ts create mode 100644 src/types/if-numeric-negative.ts create mode 100644 src/types/if-true.ts create mode 100644 src/types/intersection.ts create mode 100644 src/types/is-false.ts create mode 100644 src/types/is-index-signature.ts create mode 100644 src/types/is-integer-negative.ts create mode 100644 src/types/is-integer.ts create mode 100644 src/types/is-key-readonly.ts create mode 100644 src/types/is-literal.ts create mode 100644 src/types/is-negative.ts create mode 100644 src/types/is-numeric-negative.ts create mode 100644 src/types/is-true.ts create mode 100644 src/types/keyof.ts create mode 100644 src/types/keys-optional-exact.ts create mode 100644 src/types/keys-readonly.ts delete mode 100644 src/types/keys.ts create mode 100644 src/types/length.ts delete mode 100644 src/types/merge-defaults.ts create mode 100644 src/types/numeric-negative.ts create mode 100644 src/types/omit-index-signature.ts delete mode 100644 src/types/omit-native.ts delete mode 100644 src/types/omit.ts create mode 100644 src/types/partial-native.ts create mode 100644 src/types/partial.ts delete mode 100644 src/types/pick-native.ts delete mode 100644 src/types/pick.ts create mode 100644 src/types/reverse.ts create mode 100644 src/types/subtract.ts create mode 100644 src/types/template-data.ts delete mode 100644 src/types/tuple-length.ts delete mode 100644 src/types/tuple-to-union.ts create mode 100644 src/types/unwrap-numeric.ts diff --git a/.attw.json b/.attw.json new file mode 100644 index 00000000..d7b962c6 --- /dev/null +++ b/.attw.json @@ -0,0 +1,7 @@ +{ + "color": true, + "emoji": true, + "format": "ascii", + "ignoreRules": [], + "summary": true +} diff --git a/.codecov.yml b/.codecov.yml index b4526abf..d1911a36 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -89,6 +89,6 @@ ignore: - '**/__mocks__/**' - '**/__tests__/**' - '**/index.ts' - - src/interfaces/ - - src/types/ + - '**/interfaces' + - '**/types' - src/utils/*.options.ts diff --git a/.dictionary.txt b/.dictionary.txt index 845aba29..e7b8242a 100644 --- a/.dictionary.txt +++ b/.dictionary.txt @@ -1,4 +1,6 @@ +abcdefghijklmnopqrstuvwyz ardatan +attw bdougie booleanish cefc @@ -31,6 +33,9 @@ promisable stringafiable tryit tscu +uncapitalize unstub vates +vitest yarnrc +zyxwvutsrqponmlkjihgfedcba diff --git a/.eslintrc.base.cjs b/.eslintrc.base.cjs index 95ced980..5f733b16 100644 --- a/.eslintrc.base.cjs +++ b/.eslintrc.base.cjs @@ -1166,7 +1166,9 @@ const config = { implementsReplacesDocs: true, overrideReplacesDocs: true, preferredTypes: { - '*': false + '*': false, + '.<>': false, + 'Array<>': { replacement: '[]' } }, structuredTags: { const: { diff --git a/.eslintrc.cjs b/.eslintrc.cjs index a42fbcd6..6e7a1a3a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -13,35 +13,19 @@ const config = { overrides: [ ...require('./.eslintrc.base.cjs').overrides, { - files: [ - 'src/types/built-in.ts', - 'src/types/fn.ts', - 'src/types/simplify.ts' - ], - rules: { - '@typescript-eslint/ban-types': 0 - } - }, - { - files: ['src/types/built-in.ts'], + files: ['src/interfaces/map-like.ts'], rules: { - '@typescript-eslint/array-type': 0 + '@typescript-eslint/consistent-indexed-object-style': 0 } }, { files: [ - 'src/types/head.ts', - 'src/types/is-tuple.ts', - 'src/types/tail.ts' + 'src/types/built-in.ts', + 'src/types/fn.ts', + 'src/types/is-index-signature.ts' ], rules: { - '@typescript-eslint/no-unused-vars': 0 - } - }, - { - files: ['src/interfaces/map-like.ts'], - rules: { - '@typescript-eslint/consistent-indexed-object-style': 0 + '@typescript-eslint/ban-types': 0 } } ], diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fa57d8b4..d322c7f5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -30,6 +30,8 @@ updates: prefix: build include: scope directory: / + ignore: + - dependency-name: vitest labels: - scope:dependencies - type:build diff --git a/.github/infrastructure.yml b/.github/infrastructure.yml index ba447703..f8c23e60 100644 --- a/.github/infrastructure.yml +++ b/.github/infrastructure.yml @@ -40,7 +40,7 @@ branches: - context: test (18) - context: test (19) - context: test (20) - - context: typescript (5.1.0-beta) + - context: typescript (5.1.6) - context: typescript (latest) - context: typescript (~4.9.0) strict: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a0fabc4..12dd033f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -352,6 +352,10 @@ jobs: - gitguardian - preflight runs-on: ubuntu-latest + env: + TARFILE: | + ${{ startsWith(github.ref_name, 'release/') && format('@{0}-{1}-{2}.tgz', github.repository_owner, github.event.repository.name, needs.preflight.outputs.version) + || format('@{0}-{1}-{2}+{3}.tgz', github.repository_owner, github.event.repository.name, needs.preflight.outputs.version, github.event.pull_request.head.sha || github.sha) }} steps: - id: checkout name: Checkout ${{ env.REF_NAME }} @@ -372,12 +376,18 @@ jobs: with: key: ${{ runner.os }}-${{ github.run_id }} path: ${{ env.CACHE_PATH }} + - id: local-binaries + name: Add local binaries to $PATH + run: echo "$GITHUB_WORKSPACE/$CACHE_PATH/.bin" >> $GITHUB_PATH - id: pack name: Pack project - run: yarn pack -o %s-%v.tgz + run: yarn pack -o ${{ env.TARFILE }} - id: typecheck name: Run typecheck run: yarn check:types:build + - id: attw + name: Analyze types distribution + run: attw ${{ env.TARFILE }} - id: pkg-size-report name: Package size report run: yarn pkg-size @@ -385,6 +395,5 @@ jobs: name: Archive production artifacts uses: actions/upload-artifact@v3.1.2 with: - name: | - ${{ format('@{0}-{1}-{2}', github.repository_owner, github.event.repository.name, needs.preflight.outputs.version) }} - path: '*.tgz' + name: ${{ env.TARFILE }} + path: ${{ env.TARFILE }} diff --git a/.vscode/settings.json b/.vscode/settings.json index 5737bf3d..95706042 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -256,6 +256,7 @@ "shellformat.flag": "-ci -fn -i=2 -sr", "shellformat.useEditorConfig": true, "terminal.integrated.copyOnSelection": true, + "terminal.integrated.scrollback": 10000, "todo-tree.filtering.ignoreGitSubmodules": true, "todo-tree.filtering.includeHiddenFiles": false, "todo-tree.filtering.useBuiltInExcludes": "file and search excludes", diff --git a/__fixtures__/book.interface.ts b/__fixtures__/book.interface.ts index b64f701b..ac112a85 100644 --- a/__fixtures__/book.interface.ts +++ b/__fixtures__/book.interface.ts @@ -3,6 +3,7 @@ * @module tests/fixtures/Book */ +import type { Nullable, Simplify } from '#src/types' import type Author from './author.interface' import type Publisher from './publisher.interface' @@ -23,7 +24,7 @@ interface Book { /** * Book publisher. */ - publisher?: Publisher + publisher?: Nullable> /** * Book readers. diff --git a/__fixtures__/ordered-pair.ts b/__fixtures__/ordered-pair.ts deleted file mode 100644 index faaab457..00000000 --- a/__fixtures__/ordered-pair.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file Fixtures - OrderedPair - * @module fixtures/OrderedPair - */ - -/** - * Pair of numbers representing a single point on a two-dimensional grid. - * - * @see https://splashlearn.com/math-vocabulary/geometry/ordered-pair - */ -interface IOrderedPair { - x: number - y: number -} - -/** - * Pair of numbers representing a single point on a two-dimensional grid. - * - * @see https://splashlearn.com/math-vocabulary/geometry/ordered-pair - */ -type TOrderedPair = { - x: number - y: number -} - -export type { IOrderedPair, TOrderedPair } diff --git a/__fixtures__/vehicle.ts b/__fixtures__/vehicle.ts new file mode 100644 index 00000000..a013ee45 --- /dev/null +++ b/__fixtures__/vehicle.ts @@ -0,0 +1,31 @@ +/** + * @file Test Fixtures - Vehicle + * @module tests/fixtures/Vehicle + */ + +/** + * Object representing a vehicle. + */ +type Vehicle = { + /** + * Vehicle brand. + */ + make: string + + /** + * Vehicle model. + */ + model: string + + /** + * Vehicle identification number. + */ + vin: string + + /** + * Vehicle model year. + */ + year: number +} + +export type { Vehicle as default } diff --git a/__tests__/reporters/notifier.ts b/__tests__/reporters/notifier.ts index 14e4bcf8..9f7884bf 100644 --- a/__tests__/reporters/notifier.ts +++ b/__tests__/reporters/notifier.ts @@ -20,22 +20,28 @@ import type { File, Reporter, Task, Test, Vitest } from 'vitest' */ class Notifier implements Reporter { /** + * Test reporter context. + * * @public - * @member {Vitest} ctx - Test reporter context + * @member {Vitest} ctx */ - public ctx: Vitest = {} as Vitest + public ctx!: Vitest /** + * Test run end time (in milliseconds). + * * @public - * @member {number} end - Test run end time (in milliseconds) + * @member {number} end */ - public end: number = 0 + public end!: number /** + * Test run start time (in milliseconds). + * * @public - * @member {number} start - Test run start time (in milliseconds) + * @member {number} start */ - public start: number = 0 + public start!: number /** * Sends a notification. @@ -52,19 +58,39 @@ class Notifier implements Reporter { files: File[] = this.ctx.state.getFiles(), errors: unknown[] = this.ctx.state.getUnhandledErrors() ): Promise { - /** @const {Test[]} tests - Tests run */ + /** + * Tests that have been run. + * + * @const {Test[]} tests + */ const tests: Test[] = this.tests(files) - /** @const {number} fails - Total number of failed tests */ + /** + * Total number of failed tests. + * + * @const {number} fails + */ const fails: number = tests.filter(t => t.result?.state === 'fail').length - /** @const {number} passes - Total number of passed tests */ + /** + * Total number of passed tests. + * + * @const {number} passes + */ const passes: number = tests.filter(t => t.result?.state === 'pass').length - /** @var {string} message - Notification message */ + /** + * Notification message. + * + * @var {string} message + */ let message: string = '' - /** @var {string} title - Notification title */ + /** + * Notification title. + * + * @var {string} title + */ let title: string = '' // get notification title and message based on number of failed tests @@ -76,7 +102,11 @@ class Notifier implements Reporter { title = '\u274C Failed' } else { - /** @const {number} time - Time to run all tests (in milliseconds) */ + /** + * Time to run all tests (in milliseconds). + * + * @const {number} time + */ const time: number = this.end - this.start message = dedent` @@ -128,7 +158,7 @@ class Notifier implements Reporter { */ public onInit(context: Vitest): void { this.ctx = context - return void (this.start = performance.now()) + return void ((this.start = performance.now()) && (this.end = 0)) } /** @@ -140,17 +170,13 @@ class Notifier implements Reporter { * @return {Test[]} `Test` object array */ protected tests(tasks: OneOrMany = []): Test[] { - const { mode } = this.ctx - return (isArray(tasks) ? tasks : [tasks]).flatMap(task => { - const { type } = task - - return mode === 'typecheck' && type === 'suite' && task.tasks.length === 0 - ? ([task] as unknown as [Test]) - : type === 'test' + return task.type === 'custom' + ? [task as unknown as Test] + : task.type === 'test' ? [task] : 'tasks' in task - ? task.tasks.flatMap(t => (t.type === 'test' ? [t] : this.tests(t))) + ? task.tasks.flatMap(task => this.tests(task)) : [] }) } diff --git a/__tests__/ts/v4/tsconfig.typecheck.json b/__tests__/ts/v4/tsconfig.typecheck.json index 6ed9d5df..5385475b 100644 --- a/__tests__/ts/v4/tsconfig.typecheck.json +++ b/__tests__/ts/v4/tsconfig.typecheck.json @@ -2,13 +2,18 @@ "compilerOptions": { "target": "es2022" }, + "exclude": [ + "../../../**/coverage", + "../../../**/dist", + "../../../**/node_modules" + ], "extends": "./tsconfig.json", "include": [ - "**/**.cts", - "**/**.mts", - "**/**.ts", - "**/.*.cts", - "**/.*.mts", - "**/.*.ts" + "../../../**/**.cts", + "../../../**/**.mts", + "../../../**/**.ts", + "../../../**/.*.cts", + "../../../**/.*.mts", + "../../../**/.*.ts" ] } diff --git a/package.json b/package.json index ed7a2584..cd8efdf6 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,10 @@ "description": "TypeScript-friendly utilities", "version": "6.0.0-alpha.10", "keywords": [ + "deep", "definitions", "enums", + "get", "interfaces", "type-definitions", "type-guards", @@ -47,11 +49,12 @@ "scripts": { "build": "mkbuild", "changelog": "node --loader=./loader.mjs ./config/changelog.config", - "check:ci": "yarn dedupe --check && yarn check:format && yarn check:lint && yarn check:spelling && yarn typecheck && yarn test:cov && yarn pack -o %s-%v.tgz && yarn clean:pack && yarn check:types:build && yarn pkg-size", + "check:ci": "yarn dedupe --check && yarn check:format && yarn check:lint && yarn check:spelling && yarn typecheck && yarn test:cov && yarn pack && yarn check:types:build && attw package.tgz && yarn clean:pack && yarn pkg-size", "check:format": "prettier --check .", "check:lint": "eslint --exit-on-fatal-error --max-warnings 0 .", "check:spelling": "cspell lint --color --no-progress --relative $@ \"**\"", "check:types": "tsc -p tsconfig.typecheck.json", + "check:types:attw": "yarn pack && attw package.tgz && yarn clean:pack", "check:types:build": "bash ./scripts/typecheck-build.sh", "check:upgrades": "yarn upgrade-interactive", "clean:build": "trash ./{dist,*.tgz}", @@ -86,8 +89,9 @@ "dequal": "2.0.3" }, "devDependencies": { - "@commitlint/cli": "17.6.3", - "@faker-js/faker": "8.0.1", + "@arethetypeswrong/cli": "0.4.2", + "@commitlint/cli": "17.6.5", + "@faker-js/faker": "8.0.2", "@flex-development/commitlint-config": "1.0.1", "@flex-development/decorator-regex": "1.0.0", "@flex-development/esm-types": "1.0.0", @@ -101,22 +105,22 @@ "@types/chai-quantifiers": "1.0.1", "@types/chai-string": "1.4.2", "@types/conventional-changelog": "3.1.1", - "@types/conventional-changelog-core": "4.2.1", - "@types/conventional-changelog-writer": "4.0.2", + "@types/conventional-changelog-core": "4.2.2", + "@types/conventional-changelog-writer": "4.0.3", "@types/conventional-recommended-bump": "6.1.0", "@types/dateformat": "5.0.0", - "@types/eslint": "8.37.0", + "@types/eslint": "8.40.2", "@types/fs-extra": "11.0.1", "@types/git-raw-commits": "2.0.1", "@types/is-ci": "3.0.0", "@types/node-notifier": "8.0.2", - "@types/prettier": "2.7.2", + "@types/prettier": "2.7.3", "@types/semver": "7.5.0", - "@typescript-eslint/eslint-plugin": "5.59.7", - "@typescript-eslint/parser": "5.59.7", + "@typescript-eslint/eslint-plugin": "5.60.0", + "@typescript-eslint/parser": "5.60.0", "@vates/toggle-scripts": "1.0.0", - "@vitest/coverage-c8": "0.31.1", - "@vitest/ui": "0.31.1", + "@vitest/coverage-c8": "0.32.2", + "@vitest/ui": "0.32.2", "add-stream": "1.0.0", "chai": "5.0.0-alpha.0", "chai-each": "0.0.1", @@ -129,22 +133,22 @@ "conventional-recommended-bump": "6.1.0", "cross-env": "7.0.3", "cspell": "7.0.0-alpha.2", - "esbuild": "0.17.19", - "eslint": "8.41.0", + "esbuild": "0.18.6", + "eslint": "8.43.0", "eslint-config-prettier": "8.8.0", "eslint-plugin-chai-expect": "3.0.0", "eslint-plugin-jest-formatting": "3.1.0", "eslint-plugin-jsdoc": "44.1.0", - "eslint-plugin-jsonc": "2.8.0", + "eslint-plugin-jsonc": "2.9.0", "eslint-plugin-markdown": "3.0.0", "eslint-plugin-markdownlint": "0.4.1", "eslint-plugin-node": "11.1.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-promise": "6.1.1", "eslint-plugin-unicorn": "47.0.0", - "eslint-plugin-yml": "1.7.0", - "graphql": "16.6.0", - "graphql-config": "4.5.0", + "eslint-plugin-yml": "1.8.0", + "graphql": "16.7.0", + "graphql-config": "5.0.2", "growl": "1.10.5", "husky": "8.0.3", "is-ci": "3.0.1", @@ -158,16 +162,16 @@ "pretty-format": "29.5.0", "pupa": "3.1.0", "sade": "1.8.1", - "semver": "7.5.1", + "semver": "7.5.2", "serve": "14.2.0", "tempfile": "5.0.0", "trash-cli": "5.0.0", "ts-dedent": "2.2.0", - "typescript": "5.1.0-beta", + "typescript": "5.1.6", "version-bump-prompt": "6.1.0", - "vite": "4.3.8", + "vite": "4.3.9", "vite-tsconfig-paths": "4.2.0", - "vitest": "0.31.1", + "vitest": "0.32.2", "vitest-github-actions-reporter": "0.10.0", "yaml-eslint-parser": "1.2.2" }, @@ -180,7 +184,8 @@ } }, "resolutions": { - "@ardatan/sync-fetch": "larsgw/sync-fetch#head=worker_threads" + "@ardatan/sync-fetch": "larsgw/sync-fetch#head=worker_threads", + "vitest@npm:0.32.2": "patch:vitest@npm%3A0.32.2#patches/vitest+0.32.2.dev.patch" }, "engines": { "node": ">=16.20.0", diff --git a/patches/vitest+0.32.2.dev.patch b/patches/vitest+0.32.2.dev.patch new file mode 100644 index 00000000..71253dc2 --- /dev/null +++ b/patches/vitest+0.32.2.dev.patch @@ -0,0 +1,10 @@ +diff --git a/./dist/vendor-cli-api.f9adf98c.js b/./dist/vendor-cli-api.f9adf98c.js +index v0.32.2..v0.32.2 +--- a/./dist/vendor-cli-api.f9adf98c.js ++++ b/./dist/vendor-cli-api.f9adf98c.js +@@ -11958,3 +11958,2 @@ +- const suite = lastSuite; +- while (lastSuite !== file && lastSuite.end < index) +- lastSuite = suite.suite; ++ while (lastSuite !== file && lastSuite.end < index) ++ lastSuite = lastSuite.suite; diff --git a/src/types/__tests__/array-to-union.spec-d.ts b/src/types/__tests__/array-to-union.spec-d.ts new file mode 100644 index 00000000..0a8963a9 --- /dev/null +++ b/src/types/__tests__/array-to-union.spec-d.ts @@ -0,0 +1,42 @@ +/** + * @file Type Tests - ArrayToUnion + * @module tutils/types/tests/unit-d/ArrayToUnion + */ + +import type TestSubject from '../array-to-union' +import type NIL from '../nil' +import type Nilable from '../nilable' + +describe('unit-d:types/ArrayToUnion', () => { + it('should equal never if T is any', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends NIL', () => { + it('should equal never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + }) + + describe('T extends readonly unknown[]', () => { + it('should equal T[number]', () => { + // Arrange + type T = ['a', 'b', 'c'] + + // Expect + expectTypeOf>().toEqualTypeOf<'a' | 'b' | 'c'>() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf<0 | 1 | 2>() + }) + }) +}) diff --git a/src/types/__tests__/assign.spec-d.ts b/src/types/__tests__/assign.spec-d.ts index 90b1e28b..aab7528a 100644 --- a/src/types/__tests__/assign.spec-d.ts +++ b/src/types/__tests__/assign.spec-d.ts @@ -4,36 +4,167 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' import type TestSubject from '../assign' import type EmptyArray from '../empty-array' -import type Merge from '../merge' +import type EmptyObject from '../empty-object' +import type Nilable from '../nilable' +import type { tag } from '../opaque' +import type PropertyKey from '../property-key' describe('unit-d:types/Assign', () => { - it('should equal T if U extends EmptyArray', () => { - expectTypeOf>().toEqualTypeOf() + type T = Book + + it('should equal Record & T if U is any', () => { + // Arrange + type Expect = Record & T + + // Expect + expectTypeOf>().toEqualTypeOf() }) - it('should equal T if U extends EmptyObject', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal T if U is never', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should merge U into T if U extends ObjectCurly', () => { - // Arrange - type U = { email: Lowercase } + describe('U extends ObjectCurly', () => { + it('should assign properties of U to T', () => { + // Arrange + type U = { + [x: string]: any + readonly [tag]: 'book' + readonly id?: string + readonly publisher: string + } - // Expect - expectTypeOf>().toEqualTypeOf>() + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & U + >() + }) + + it('should equal T if Keyof is never', () => { + expectTypeOf>().toEqualTypeOf() + }) }) - it('should merge U into T if U extends readonly ObjectCurly[]', () => { - // Arrange - type U1 = [{ display_name: string }, { email: Lowercase }] - type U2 = { display_name: string; email: Lowercase }[] - type E1 = Merge, U1[1]> - type E2 = Merge + describe('U extends readonly ObjectCurly[]', () => { + it('should equal T if U extends readonly EmptyObject[]', () => { + // Arrange + type U1 = EmptyObject[] + type U2 = [EmptyObject, EmptyObject, EmptyObject] + type U3 = readonly EmptyObject[] + type U4 = readonly [EmptyObject, EmptyObject, EmptyObject] - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('IsLiteral> extends true', () => { + it('should equal T if U extends Readonly', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + }) + + it('should assign properties of U[i] to T', () => { + // Arrange + type U = [ + { readonly [tag]: 'book' }, + { readonly id: string }, + { readonly publisher?: string }, + any, + never, + { readonly a: string }, + { readonly b: string }, + { readonly c: string }, + { readonly d: string }, + { readonly e: string }, + { readonly f: string }, + { readonly g: string }, + { readonly h: string }, + { readonly i: string }, + { readonly j: string }, + { readonly k: string }, + { readonly m: string }, + { readonly n: string }, + { readonly o: string }, + { readonly p: string }, + { readonly q: string }, + { readonly r: string }, + { readonly s: string }, + { readonly t: string }, + { readonly u: string }, + { readonly v: string }, + { readonly w: string }, + { readonly x: string }, + { readonly y: string }, + { readonly z: string } + ] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & { + [x: string]: any + [x: number]: any + [x: symbol]: any + readonly [tag]: 'book' + readonly a: string + readonly b: string + readonly c: string + readonly d: string + readonly e: string + readonly f: string + readonly g: string + readonly h: string + readonly i: string + readonly id: string + readonly j: string + readonly k: string + readonly m: string + readonly n: string + readonly o: string + readonly p: string + readonly publisher?: string + readonly q: string + readonly r: string + readonly s: string + readonly t: string + readonly u: string + readonly v: string + readonly w: string + readonly x: string + readonly y: string + readonly z: string + } + >() + }) + }) + + describe('number extends Length', () => { + it('should assign properties of U[0] to T', () => { + // Arrange + type U = { readonly [tag]: 'book'; readonly id?: string }[] + type Expect = Omit & U[0] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable + type U = { readonly [tag]: string } | { readonly id: string } + + // Expect + expectTypeOf>().toEqualTypeOf< + (T & { readonly [tag]: string }) | (T & { readonly id: string }) + >() + }) }) }) diff --git a/src/types/__tests__/at.spec-d.ts b/src/types/__tests__/at.spec-d.ts index bd76906c..3d4dbd85 100644 --- a/src/types/__tests__/at.spec-d.ts +++ b/src/types/__tests__/at.spec-d.ts @@ -3,65 +3,319 @@ * @module tutils/types/tests/unit-d/At */ -import type Author from '#fixtures/author.interface' +import type Vehicle from '#fixtures/vehicle' import type TestSubject from '../at' import type EmptyArray from '../empty-array' import type EmptyString from '../empty-string' +import type Fallback from '../fallback' +import type Integer from '../integer' +import type Nilable from '../nilable' +import type Numeric from '../numeric' +import type NegativeNumeric from '../numeric-negative' +import type Optional from '../optional' +import type Reverse from '../reverse' +import type Split from '../split' describe('unit-d:types/At', () => { - type F = undefined - - it('should equal F if K is out of range', () => { - // Arrange - type T1 = ['a', 'b', 'c'] - type T2 = 'abc' - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + type F = null + + it('should equal F if T is any', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal F if T extends EmptyString', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf, 0>>().toEqualTypeOf() + it('should equal F if T is never', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal F if T extends EmptyArray', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal Fallback if Has extends true', () => { + // Arrange + type T = { '3': number | undefined } & [Vehicle] + type K = '3' | 3 + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal Fallback if K is any', () => { + // Arrange + type T = ['a', 'b', 'c'?] + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('Stringify extends `-${infer N extends number}`', () => { + it('should equal F if K is out of range', () => { + // Arrange + type T = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('NegativeNumeric extends K', () => { + it('should equal Fallback', () => { + // Arrange + type T = [number?, number?] + type K = NegativeNumeric + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('Stringify extends Stringify>', () => { + it('should equal Fallback<[any, ...Reverse][N], F>', () => { + // Arrange + type T = ['a'?, 'b'?, 'c'?, 'd'?, 'e'?] + type Expect = Fallback<[any, ...Reverse][3], F> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('Stringify extends `${infer N extends number}`', () => { + it('should equal F if N is out of range', () => { + // Arrange + type T = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends Integer', () => { + it('should equal Fallback', () => { + // Arrange + type T = [Vehicle['vin'], Vehicle, number?] + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('N extends Indices', () => { + it('should equal Fallback', () => { + // Arrange + type T = ['a'?, 'b'?, 'c'?, 'd'?, 'e'?] + type K = '1' | 1 + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('Numeric extends K', () => { + it('should equal Fallback', () => { + // Arrange + type T = [number?, number?] + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends K', () => { + it('should equal Fallback', () => { + // Arrange + type T = [number?, string?] + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('number extends Length', () => { + type T = Vehicle[] + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal Fallback, F>', () => { + // Arrange + type Expect = Fallback, F> + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal Fallback if Has extends true', () => { + // Arrange + type T = Vehicle[] & { '3': number | undefined } + type K = '3' | 3 + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) }) - it('should equal F | T[number] if T is not string literal or tuple', () => { - // Arrange - type T1 = Author[] - type T2 = string + describe('T extends string', () => { + describe('IsLiteral> extends true', () => { + type Splitter = Split + + it('should equal Splitter[number] if K is any', () => { + // Arrange + type T = 'abc' + type Expect = Splitter[number] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('Stringify extends `-${infer N extends number}`', () => { + it('should equal F if N is out of range', () => { + // Arrange + type T = EmptyString + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('NegativeNumeric extends K', () => { + it('should equal Splitter[number]', () => { + // Arrange + type T = 'abc' + type K = NegativeNumeric + type Expect = Splitter[number] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('Stringify extends Stringify>', () => { + it('should equal [any, ...Reverse>][N]', () => { + // Arrange + type T = 'xyz' + type Expect = [any, ...Reverse>][3] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('Stringify extends `${infer N extends number}`', () => { + it('should equal F if N is out of range', () => { + // Arrange + type T = EmptyString + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends Integer', () => { + it('should equal Splitter[number]', () => { + // Arrange + type T = 'abc' + type Expect = Splitter[number] - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('N extends Indices', () => { + it('should equal Splitter[N]', () => { + // Arrange + type T = 'xyz' + type K = '2' | 2 + type Expect = Splitter[K] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('Numeric extends K', () => { + it('should equal Splitter[number]', () => { + // Arrange + type T = 'racecar' + type Expect = Splitter[number] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends K', () => { + it('should equal Splitter[number]', () => { + // Arrange + type T = 'racecar' + type Expect = Splitter[number] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('number extends Length', () => { + type T = string + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal Fallback, F>', () => { + // Arrange + type Expect = Fallback, F> + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal Fallback if Has extends true', () => { + // Arrange + type T = string & { '3': number | undefined } + type K = '3' | 3 + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) }) - it('should equal T.at(K)! if T is string literal or tuple', () => { - // Arrange - type T1 = [Author, Author] - type T2 = 'def' - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf<'e'>() - expectTypeOf>().toEqualTypeOf<'d' | 'e' | 'f'>() - expectTypeOf>().toEqualTypeOf<'e'>() - expectTypeOf>().toEqualTypeOf<'d' | 'e' | 'f'>() + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable<'xyz' | ['a', 'b', 'c'?]> + type K = -1 | 0 + type Expect = Fallback<'a' | 'c' | 'x' | 'z' | undefined, F> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/booleanish.spec-d.ts b/src/types/__tests__/booleanish.spec-d.ts index e6f3455f..4c4ad5a2 100644 --- a/src/types/__tests__/booleanish.spec-d.ts +++ b/src/types/__tests__/booleanish.spec-d.ts @@ -11,6 +11,14 @@ describe('unit-d:types/Booleanish', () => { expectTypeOf().extract>().toBeString() }) + it('should extract 0', () => { + expectTypeOf().extract<0>().toBeNumber() + }) + + it('should extract 1', () => { + expectTypeOf().extract<1>().toBeNumber() + }) + it('should extract boolean', () => { expectTypeOf().extract().toBeBoolean() }) diff --git a/src/types/__tests__/built-in.spec-d.ts b/src/types/__tests__/built-in.spec-d.ts index 8d3ac36e..48e83859 100644 --- a/src/types/__tests__/built-in.spec-d.ts +++ b/src/types/__tests__/built-in.spec-d.ts @@ -10,242 +10,200 @@ import type TypedArray from '../typed-array' describe('unit-d:types/BuiltIn', () => { it('should extract AggregateError', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() - }) - - it('should extract Array', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract ArrayBuffer', () => { - expectTypeOf() - .extract() - .toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract AsyncGenerator', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract AsyncGeneratorFunction', () => { expectTypeOf() .extract() - .toEqualTypeOf() + .not.toBeNever() }) it('should extract AsyncIterator', () => { - expectTypeOf() - .extract>() - .toMatchTypeOf>() + expectTypeOf().extract>().not.toBeNever() }) it('should extract Atomics', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract DataView', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Date', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Error', () => { - expectTypeOf().extract().toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract EvalError', () => { - expectTypeOf().extract().toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract FinalizationRegistry', () => { expectTypeOf() .extract>() - .toEqualTypeOf>() + .not.toBeNever() }) it('should extract Fn', () => { - expectTypeOf().extract().toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() + }) + + it('should extract Function', () => { + expectTypeOf().extract().not.toBeNever() }) it('should extract Generator', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract GeneratorFunction', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.Collator', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.DateTimeFormat', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.DisplayNames', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.ListFormat', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.Locale', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.NumberFormat', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.PluralRules', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Intl.RelativeTimeFormat', () => { expectTypeOf() .extract() - .toEqualTypeOf() + .not.toBeNever() }) it('should extract Intl.Segmenter', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Iterator', () => { - expectTypeOf() - .extract>() - .toMatchTypeOf>() + expectTypeOf().extract>().not.toBeNever() }) it('should extract JSON', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Map', () => { - expectTypeOf() - .extract>() - .toEqualTypeOf>() + expectTypeOf().extract>().not.toBeNever() }) it('should extract Math', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Primitive', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Promise', () => { - expectTypeOf() - .extract>() - .toEqualTypeOf>() + expectTypeOf().extract>().not.toBeNever() }) it('should extract RangeError', () => { - expectTypeOf() - .extract() - .toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() + }) + + it('should extract Readonly', () => { + expectTypeOf().extract>().not.toBeNever() }) it('should extract ReferenceError', () => { - expectTypeOf() - .extract() - .toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract RegExp', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract Set', () => { - expectTypeOf().extract>().toEqualTypeOf>() + expectTypeOf().extract>().not.toBeNever() }) it('should extract SharedArrayBuffer', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract SyntaxError', () => { - expectTypeOf() - .extract() - .toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract TypedArray', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract TypeError', () => { - expectTypeOf().extract().toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract URIError', () => { - expectTypeOf().extract().toMatchTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract WeakMap', () => { - expectTypeOf() - .extract>() - .toMatchTypeOf>() + expectTypeOf().extract>().not.toBeNever() }) it('should extract WeakRef', () => { - expectTypeOf() - .extract>() - .toEqualTypeOf>() + expectTypeOf().extract>().not.toBeNever() }) it('should extract WeakSet', () => { - expectTypeOf() - .extract>() - .toMatchTypeOf>() + expectTypeOf().extract>().not.toBeNever() + }) + + it('should extract any[]', () => { + expectTypeOf().extract().not.toBeNever() + }) + + it('should extract readonly any[]', () => { + expectTypeOf().extract().not.toBeNever() }) it('should extract typeof Intl', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract typeof Proxy', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract typeof Reflect', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/class-abstract.spec-d.ts b/src/types/__tests__/class-abstract.spec-d.ts index 817a8666..f2f48323 100644 --- a/src/types/__tests__/class-abstract.spec-d.ts +++ b/src/types/__tests__/class-abstract.spec-d.ts @@ -3,14 +3,27 @@ * @module tutils/types/tests/unit-d/AbstractClass */ +import type Vehicle from '#fixtures/vehicle' import type TestSubject from '../class-abstract' +import type AbstractConstructor from '../constructor-abstract' describe('unit-d:types/AbstractClass', () => { - abstract class Person { - constructor(public first_name: string, public last_name: string) {} - } + type T = Vehicle - it('should match abstract class declaration', () => { - assertType>(Person) + it('should extend AbstractConstructor', () => { + // Arrange + type A = [ + Vehicle['vin'], + Vehicle['make'], + Vehicle['model'], + Vehicle['year'] + ] + + // Expect + expectTypeOf>().toMatchTypeOf>() + }) + + it('should match [prototype: T]', () => { + expectTypeOf>().toMatchTypeOf<{ prototype: T }>() }) }) diff --git a/src/types/__tests__/class.spec-d.ts b/src/types/__tests__/class.spec-d.ts index 41f308eb..5559a06c 100644 --- a/src/types/__tests__/class.spec-d.ts +++ b/src/types/__tests__/class.spec-d.ts @@ -3,14 +3,22 @@ * @module tutils/types/tests/unit-d/Class */ +import type Vehicle from '#fixtures/vehicle' import type TestSubject from '../class' +import type Constructor from '../constructor' describe('unit-d:types/Class', () => { - class Subscriber { - constructor(public email: string, public name: string) {} - } + type T = Vehicle - it('should match class declaration', () => { - assertType>(Subscriber) + it('should extend Constructor', () => { + // Arrange + type A = [Vehicle['vin']] + + // Expect + expectTypeOf>().toMatchTypeOf>() + }) + + it('should match [prototype: T]', () => { + expectTypeOf>().toMatchTypeOf<{ prototype: T }>() }) }) diff --git a/src/types/__tests__/constructor-abstract.spec-d.ts b/src/types/__tests__/constructor-abstract.spec-d.ts index 01711a68..d53fabff 100644 --- a/src/types/__tests__/constructor-abstract.spec-d.ts +++ b/src/types/__tests__/constructor-abstract.spec-d.ts @@ -3,19 +3,22 @@ * @module tutils/types/tests/unit-d/AbstractConstructor */ +import type Vehicle from '#fixtures/vehicle' import type TestSubject from '../constructor-abstract' describe('unit-d:types/AbstractConstructor', () => { - abstract class Vehicle { - constructor( - public readonly vin: number, - public readonly make: string, - public readonly model: string, - public readonly year: number - ) {} - } + it('should equal abstract new (...args: A) => T', () => { + // Arrange + type A = [ + Vehicle['vin'], + Vehicle['make'], + Vehicle['model'], + Vehicle['year'] + ] - it('should match abstract class declaration', () => { - assertType>(Vehicle) + // Expect + expectTypeOf>().toEqualTypeOf< + abstract new (...args: A) => Vehicle + >() }) }) diff --git a/src/types/__tests__/constructor.spec-d.ts b/src/types/__tests__/constructor.spec-d.ts index 1ccfb769..0170ae33 100644 --- a/src/types/__tests__/constructor.spec-d.ts +++ b/src/types/__tests__/constructor.spec-d.ts @@ -3,14 +3,16 @@ * @module tutils/types/tests/unit-d/Constructor */ +import type Vehicle from '#fixtures/vehicle' import type TestSubject from '../constructor' describe('unit-d:types/Constructor', () => { - class Dog { - constructor(public name: string) {} - } + it('should equal new (...args: A) => T', () => { + // Arrange + type T = Vehicle + type A = [Vehicle['vin']] - it('should match class declaration', () => { - assertType>(Dog) + // Expect + expectTypeOf>().toEqualTypeOf T>() }) }) diff --git a/src/types/__tests__/continuous-value.spec-d.ts b/src/types/__tests__/continuous-value.spec-d.ts index 1079df23..9d33eaed 100644 --- a/src/types/__tests__/continuous-value.spec-d.ts +++ b/src/types/__tests__/continuous-value.spec-d.ts @@ -7,10 +7,10 @@ import type TestSubject from '../continuous-value' describe('unit-d:types/ContinuousValue', () => { it('should extract Date', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract number', () => { - expectTypeOf().extract().toBeNumber() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/dot.spec-d.ts b/src/types/__tests__/dot.spec-d.ts new file mode 100644 index 00000000..2eef2e40 --- /dev/null +++ b/src/types/__tests__/dot.spec-d.ts @@ -0,0 +1,12 @@ +/** + * @file Type Tests - Dot + * @module tutils/types/tests/unit-d/Dot + */ + +import type TestSubject from '../dot' + +describe('unit-d:types/Dot', () => { + it('should equal "."', () => { + expectTypeOf().toEqualTypeOf<'.'>() + }) +}) diff --git a/src/types/__tests__/empty-array.spec-d.ts b/src/types/__tests__/empty-array.spec-d.ts index 635b5908..4b38a1eb 100644 --- a/src/types/__tests__/empty-array.spec-d.ts +++ b/src/types/__tests__/empty-array.spec-d.ts @@ -6,13 +6,7 @@ import type TestSubject from '../empty-array' describe('unit-d:types/EmptyArray', () => { - it('should extract Readonly<[]>', () => { - expectTypeOf().extract>().not.toBeNever() - expectTypeOf>().toMatchTypeOf() - }) - - it('should extract []', () => { - expectTypeOf().extract<[]>().not.toBeNever() - expectTypeOf<[]>().toMatchTypeOf() + it('should equal []', () => { + expectTypeOf().toEqualTypeOf<[]>() }) }) diff --git a/src/types/__tests__/empty-object.spec-d.ts b/src/types/__tests__/empty-object.spec-d.ts index 8d8e0422..96c6f96c 100644 --- a/src/types/__tests__/empty-object.spec-d.ts +++ b/src/types/__tests__/empty-object.spec-d.ts @@ -4,14 +4,10 @@ */ import type TestSubject from '../empty-object' -import type PropertyKey from '../property-key' +import type { tag } from '../empty-object' describe('unit-d:types/EmptyObject', () => { - it('should have keys of type PropertyKey', () => { - expectTypeOf().toEqualTypeOf() - }) - - it('should have properties of type never', () => { - expectTypeOf().toBeNever() + it('should match [[tag]?: never]', () => { + expectTypeOf().toBeUndefined() }) }) diff --git a/src/types/__tests__/ensure-string.spec-d.ts b/src/types/__tests__/ensure-string.spec-d.ts deleted file mode 100644 index f6d51d93..00000000 --- a/src/types/__tests__/ensure-string.spec-d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @file Type Tests - EnsureString - * @module tutils/types/tests/unit-d/EnsureString - */ - -import type TestSubject from '../ensure-string' - -describe('unit-d:types/EnsureString', () => { - it('should equal T & string', () => { - expectTypeOf>().toEqualTypeOf() - }) -}) diff --git a/src/types/__tests__/fallback.spec-d.ts b/src/types/__tests__/fallback.spec-d.ts index 2fcd0d36..851f8e8b 100644 --- a/src/types/__tests__/fallback.spec-d.ts +++ b/src/types/__tests__/fallback.spec-d.ts @@ -4,21 +4,27 @@ */ import type TestSubject from '../fallback' -import type Optional from '../optional' +import type NIL from '../nil' +import type Nilable from '../nilable' describe('unit-d:types/Fallback', () => { - it('should equal Exclude | F if T extends Condition', () => { + type C = NIL + + it('should equal Exclude | F if T extends C', () => { // Arrange - type Condition = undefined - type Expected = Exclude | F - type F = null - type T = Optional + type T = Nilable + type F = number // Expect - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf | F>() }) - it('should equal T if T does not extend Condition', () => { - expectTypeOf>().toBeString() + it('should equal T if T does not extend C', () => { + // Arrange + type T = string + type F = null + + // Expect + expectTypeOf>().toEqualTypeOf() }) }) diff --git a/src/types/__tests__/get.spec-d.ts b/src/types/__tests__/get.spec-d.ts index 3fa09901..7bc64989 100644 --- a/src/types/__tests__/get.spec-d.ts +++ b/src/types/__tests__/get.spec-d.ts @@ -5,75 +5,458 @@ import type Author from '#fixtures/author.interface' import type Book from '#fixtures/book.interface' +import type Vehicle from '#fixtures/vehicle' import type At from '../at' import type EmptyArray from '../empty-array' import type EmptyObject from '../empty-object' import type EmptyString from '../empty-string' +import type Fallback from '../fallback' +import type Fn from '../fn' import type TestSubject from '../get' -import type Optional from '../optional' +import type NIL from '../nil' +import type Nullable from '../nullable' +import type { tag } from '../opaque' +import type Primitive from '../primitive' describe('unit-d:types/Get', () => { - type F = undefined + type F = null - it('should equal At if T extends readonly unknown[]', () => { + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal Fallback K is any', () => { // Arrange - type T1 = [Author] - type T2 = Author[] + type T = Book + type Expect = Fallback - // Expect - expectTypeOf>().toEqualTypeOf>() - expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf() }) - it('should equal At if T extends string', () => { + it('should equal T if T is any', () => { // Arrange - type T1 = 'abc' - type T2 = string + type T = any // Expect - expectTypeOf>().toEqualTypeOf>() - expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toBeAny() }) - it('should equal F if T extends EmptyArray', () => { - expectTypeOf>().toEqualTypeOf() + describe('T extends ObjectCurly', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if Keyof is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Keyof', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = { res: { data: { 0: 1; book: Book; id: 'book' } } } + type K1 = 'res.data.0' + type K2 = 'res.data.book.publisher' + type K3 = 'res.data.book.title' + type K4 = 'res.data.book.title.charAt' + type E1 = Fallback + type E2 = Fallback + type E3 = Fallback + type E4 = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + it('should equal Fallback', () => { + // Arrange + type T1 = Author + type T2 = { [x: string]: string | undefined } + type K1 = 'email' + type K2 = '1' | 0 + type E1 = Fallback + type E2 = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) }) - it('should equal F if T extends EmptyObject', () => { - expectTypeOf>().toEqualTypeOf() + describe('T extends Primitive', () => { + it('should equal F if Keyof is never', () => { + // Arrange + type K = 'toString' + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends bigint', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Keyof', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = bigint & { res: { 0: { message?: string } } } + type K = 'res.0.message' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + it('should equal Fallback', () => { + // Arrange + type T = bigint + type K1 = 'toString' + type K2 = typeof Symbol.toStringTag + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + }) + + describe('T extends boolean', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Keyof', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = boolean & { res: { 0: { message?: string } } } + type K = 'res.0.message' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + it('should equal Fallback', () => { + // Arrange + type T = boolean + type K = 'valueOf' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends number', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Keyof', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = number & { res: { 0: { message?: string } } } + type K = 'res.0.message' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + it('should equal Fallback', () => { + // Arrange + type T = number + type K = 'toPrecision' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + // Arrange + type T = 'vehicle' + type K = '-8.0' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Numeric', () => { + it('should equal Fallback, R, F>, F>', () => { + // Arrange + type T = 'book' + type K1 = '-1.charCodeAt' + type K2 = '0.-1' + type E1 = Fallback['charCodeAt'], F> + type E2 = Fallback, -1>, F> + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('H extends keyof T', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = string & { res: { 0: { message?: string } } } + type K = 'res.0.message' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + describe('K extends NumberLike', () => { + it('should equal At', () => { + // Arrange + type T1 = string + type T2 = 'flex' + type T3 = EmptyString + type K = '-1' | 0 + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + + describe('K extends keyof T', () => { + it('should equal Fallback', () => { + // Arrange + type T = string + type K1 = 'charAt' + type K2 = typeof Symbol.iterator + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + }) + }) + + describe('T extends symbol', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Keyof', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = symbol & { res: { 0: { message?: string } } } + type K = 'res.0.message' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + it('should equal Fallback', () => { + // Arrange + type T = symbol + type K1 = 'toString' + type K2 = typeof Symbol.toPrimitive + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + }) }) - it('should equal F if T extends EmptyString', () => { - expectTypeOf>().toEqualTypeOf() + describe('T extends Readonly', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf, 'data', F>>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Keyof', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = Readonly & { res: { 0: { message?: string } } } + type K = 'res.0.message' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + it('should equal Fallback', () => { + // Arrange + type T = Readonly + type K1 = 'bind' + type K2 = typeof Symbol.hasInstance + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) }) - it('should equal T[K & keyof T] if K extends keyof T', () => { - // Arrange - type K = 'email' + describe('T extends readonly unknown[]', () => { + it('should equal F if K is not path of T', () => { + expectTypeOf>().toEqualTypeOf() + }) - // Expect - expectTypeOf>().toEqualTypeOf() + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal F if H does not extend Keyof', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('H extends Numeric', () => { + it('should equal Fallback, R, F>, F>', () => { + // Arrange + type T = [Author, Author?] + type K1 = '0.display_name' + type K2 = '0.email' + type K3 = '-1.display_name' + type K4 = '-1.email' + type E1 = Fallback + type E2 = Fallback + type E3 = Fallback['display_name'], F> + type E4 = Fallback['email'], F> + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('H extends Keyof', () => { + it('should equal Fallback, F>', () => { + // Arrange + type T = Vehicle[] & { res: { 0: { message?: string } } } + type K = 'res.0.message' + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('K extends Keyof', () => { + describe('K extends NumberLike', () => { + it('should equal At', () => { + // Arrange + type T1 = Author[] + type T2 = [Author, Author?] + type T3 = EmptyArray + type T4 = Readonly + type K = -1 | '1' + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + + describe('K extends keyof T', () => { + it('should equal Fallback', () => { + // Arrange + type T = Author[] + type K1 = 'push' + type K2 = typeof Symbol.iterator + type Expect = Fallback + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + }) }) - it('should equal T[K] with respect for dot notation', () => { - // Arrange - type T = Omit & { - authors: [Author] - donated_by?: { email: Lowercase; name: string } - } - type K1 = 'authors.0' - type K2 = 'authors.0.display_name' - type K3 = 'authors.0.email' - type K4 = 'authors.-1.email' - type K5 = 'authors.-2.email' - type K6 = 'donated_by.name' + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Primitive | (Vehicle & { readonly [tag]: 'vehicle' }) + type K = typeof tag | 'toString' + type Expect = Nullable< + Exclude, boolean>['toString'] | 'vehicle' + > - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf>() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toBeUndefined() - expectTypeOf>().toEqualTypeOf>() + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/has-keys.spec-d.ts b/src/types/__tests__/has-keys.spec-d.ts new file mode 100644 index 00000000..7ca8a125 --- /dev/null +++ b/src/types/__tests__/has-keys.spec-d.ts @@ -0,0 +1,36 @@ +/** + * @file Type Tests - HasKeys + * @module tutils/types/tests/unit-d/HasKeys + */ + +import type Vehicle from '#fixtures/vehicle' +import type EmptyObject from '../empty-object' +import type TestSubject from '../has-keys' +import type NIL from '../nil' +import type Primitive from '../primitive' + +describe('unit-d:types/HasKeys', () => { + it('should equal false if [Keyof] extends [never]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if [Keyof] does not extend [never]', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/head.spec-d.ts b/src/types/__tests__/head.spec-d.ts index ff085f5c..9919d6d1 100644 --- a/src/types/__tests__/head.spec-d.ts +++ b/src/types/__tests__/head.spec-d.ts @@ -3,19 +3,84 @@ * @module tutils/types/tests/unit-d/Head */ -import type Digit from '../digit' +import type Dot from '../dot' +import type EmptyArray from '../empty-array' +import type EmptyString from '../empty-string' import type TestSubject from '../head' +import type Nilable from '../nilable' +import type Optional from '../optional' describe('unit-d:types/Head', () => { - it('should equal T.slice(0, T.indexOf(D)) if T extends string', () => { - expectTypeOf>().toEqualTypeOf<'foo'>() + it('should equal never if T is any', () => { + expectTypeOf>().toBeNever() }) - it('should equal T.unshift() if T extends readonly unknown[]', () => { - expectTypeOf>().toEqualTypeOf<1>() + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() }) - it('should equal never if T is not an array or string', () => { - expectTypeOf>().toBeNever() + describe('T extends readonly unknown[]', () => { + it('should equal never if T extends Readonly', () => { + expectTypeOf>().toBeNever() + expectTypeOf>>().toBeNever() + }) + + describe('T extends readonly [(infer H)?, ...T[number][]]', () => { + it('should equal Optional', () => { + // Arrange + type T = string[] + type Expect = Optional + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + }) + }) + + describe('T extends readonly [infer H, ...T[number][]]', () => { + it('should equal T[0]', () => { + // Arrange + type T = [1, 2, 3] + type Expect = T[0] + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + it('should equal never if T extends EmptyString', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends `${infer H}${D}${string}`', () => { + it('should equal H', () => { + expectTypeOf>().toEqualTypeOf<'a'>() + expectTypeOf>().toEqualTypeOf<'a'>() + }) + }) + + describe('string extends T', () => { + it('should equal T', () => { + // Arrange + type T = string + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = number[] | 'data.message' | [null, number] + type Expect = Nilable + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-any-or-never.spec-d.ts b/src/types/__tests__/if-any-or-never.spec-d.ts index 63fa2bee..834bf82a 100644 --- a/src/types/__tests__/if-any-or-never.spec-d.ts +++ b/src/types/__tests__/if-any-or-never.spec-d.ts @@ -6,16 +6,15 @@ import type TestSubject from '../if-any-or-never' describe('unit-d:types/IfAnyOrNever', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsAnyOrNever extends false', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsAnyOrNever extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsAnyOrNever extends true', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal T if IsAnyOrNever extends true', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) }) diff --git a/src/types/__tests__/if-any.spec-d.ts b/src/types/__tests__/if-any.spec-d.ts index a6bb5f5e..c21881ed 100644 --- a/src/types/__tests__/if-any.spec-d.ts +++ b/src/types/__tests__/if-any.spec-d.ts @@ -6,14 +6,14 @@ import type TestSubject from '../if-any' describe('unit-d:types/IfAny', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsAny extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsAny extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsAny extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal T if IsAny extends true', () => { + expectTypeOf>().toEqualTypeOf() }) }) diff --git a/src/types/__tests__/if-array.spec-d.ts b/src/types/__tests__/if-array.spec-d.ts index f357ce2c..18a5c3de 100644 --- a/src/types/__tests__/if-array.spec-d.ts +++ b/src/types/__tests__/if-array.spec-d.ts @@ -4,17 +4,32 @@ */ import type TestSubject from '../if-array' +import type OneOrMany from '../one-or-many' describe('unit-d:types/IfArray', () => { - type False = false - type True = true + type F = 0 + type T = 1 type V = string - it('should equal False if IsArray extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsArray extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsArray extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsArray extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf, V, T, F>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-big-int.spec-d.ts b/src/types/__tests__/if-big-int.spec-d.ts index 2df64363..b55d8165 100644 --- a/src/types/__tests__/if-big-int.spec-d.ts +++ b/src/types/__tests__/if-big-int.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-big-int' +import type Primitive from '../primitive' describe('unit-d:types/IfBigInt', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsBigInt extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsBigInt extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsBigInt extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsBigInt extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-boolean.spec-d.ts b/src/types/__tests__/if-boolean.spec-d.ts index fcf577d3..df967bf8 100644 --- a/src/types/__tests__/if-boolean.spec-d.ts +++ b/src/types/__tests__/if-boolean.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-boolean' +import type Primitive from '../primitive' describe('unit-d:types/IfBoolean', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsBoolean extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsBoolean extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsBoolean extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsBoolean extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-equal.spec-d.ts b/src/types/__tests__/if-equal.spec-d.ts index 6003eb07..bb7e098a 100644 --- a/src/types/__tests__/if-equal.spec-d.ts +++ b/src/types/__tests__/if-equal.spec-d.ts @@ -8,17 +8,17 @@ import type Digit from '../digit' import type TestSubject from '../if-equal' describe('unit-d:types/IfEqual', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsEqual extends false', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsEqual extends false', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsEqual extends true', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal T if IsEqual extends true', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) }) diff --git a/src/types/__tests__/if-false.spec-d.ts b/src/types/__tests__/if-false.spec-d.ts new file mode 100644 index 00000000..0dc33bcb --- /dev/null +++ b/src/types/__tests__/if-false.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IfFalse + * @module tutils/types/tests/unit-d/IfFalse + */ + +import type TestSubject from '../if-false' + +describe('unit-d:types/IfFalse', () => { + type F = 0 + type T = 1 + + it('should equal F if IsFalse extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsFalse extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-function.spec-d.ts b/src/types/__tests__/if-function.spec-d.ts index 74e6d6d5..660dd74c 100644 --- a/src/types/__tests__/if-function.spec-d.ts +++ b/src/types/__tests__/if-function.spec-d.ts @@ -5,16 +5,31 @@ import type Fn from '../fn' import type TestSubject from '../if-function' +import type Nilable from '../nilable' describe('unit-d:types/IfFunction', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsFunction extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsFunction extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsFunction extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsFunction extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf, T, F>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-index-signature.spec-d.ts b/src/types/__tests__/if-index-signature.spec-d.ts new file mode 100644 index 00000000..ab2d2bc9 --- /dev/null +++ b/src/types/__tests__/if-index-signature.spec-d.ts @@ -0,0 +1,54 @@ +/** + * @file Type Tests - IfIndexSignature + * @module tutils/types/tests/unit-d/IfIndexSignature + */ + +import type TestSubject from '../if-index-signature' +import type Nilable from '../nilable' +import type PropertyKey from '../property-key' + +describe('unit-d:types/IfIndexSignature', () => { + type F = 0 + type T = 1 + + it('should equal F if IsIndexSignature extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsIndexSignature extends true', () => { + // Arrange + type U = { [x: number]: any; [x: string]: any; [x: symbol]: any } + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type U = Nilable + type K = PropertyKey + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-integer-negative.spec-d.ts b/src/types/__tests__/if-integer-negative.spec-d.ts new file mode 100644 index 00000000..b12fe564 --- /dev/null +++ b/src/types/__tests__/if-integer-negative.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IfNegativeInteger + * @module tutils/types/tests/unit-d/IfNegativeInteger + */ + +import type TestSubject from '../if-integer-negative' + +describe('unit-d:types/IfNegativeInteger', () => { + type F = 0 + type T = 1 + + it('should equal F if IsNegativeInteger extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsNegativeInteger extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-integer.spec-d.ts b/src/types/__tests__/if-integer.spec-d.ts new file mode 100644 index 00000000..957c6f36 --- /dev/null +++ b/src/types/__tests__/if-integer.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IfInteger + * @module tutils/types/tests/unit-d/IfInteger + */ + +import type TestSubject from '../if-integer' + +describe('unit-d:types/IfInteger', () => { + type F = 0 + type T = 1 + + it('should equal F if IsInteger extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsInteger extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-json-primitive.spec-d.ts b/src/types/__tests__/if-json-primitive.spec-d.ts index ac2cb74a..e9e8a82b 100644 --- a/src/types/__tests__/if-json-primitive.spec-d.ts +++ b/src/types/__tests__/if-json-primitive.spec-d.ts @@ -5,20 +5,31 @@ import type TestSubject from '../if-json-primitive' import type JsonPrimitive from '../json-primitive' +import type Primitive from '../primitive' describe('unit-d:types/IfJsonPrimitive', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsJsonPrimitive extends false', () => { - expectTypeOf< - TestSubject - >().toEqualTypeOf() + it('should equal F if IsJsonPrimitive extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsJsonPrimitive extends true', () => { - expectTypeOf< - TestSubject - >().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsJsonPrimitive extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-key-optional-exact.spec-d.ts b/src/types/__tests__/if-key-optional-exact.spec-d.ts index 22e94452..a6f37ec4 100644 --- a/src/types/__tests__/if-key-optional-exact.spec-d.ts +++ b/src/types/__tests__/if-key-optional-exact.spec-d.ts @@ -4,25 +4,45 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' import type TestSubject from '../if-key-optional-exact' describe('unit-d:types/IfExactOptionalKey', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsExactOptionalKey extends false', () => { - // Arrange - type K = 'display_name' + it('should equal F if IsExactOptionalKey extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) - // Expect - expectTypeOf>().toEqualTypeOf() + it('should equal T if IsExactOptionalKey extends true', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsExactOptionalKey extends true', () => { - // Arrange - type K = 'email' + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type U = Author | Book + type K = 'display_name' | 'email' | 'publisher' - // Expect - expectTypeOf>().toEqualTypeOf() + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-key-optional.spec-d.ts b/src/types/__tests__/if-key-optional.spec-d.ts index c0bb3975..d945328e 100644 --- a/src/types/__tests__/if-key-optional.spec-d.ts +++ b/src/types/__tests__/if-key-optional.spec-d.ts @@ -4,25 +4,45 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' import type TestSubject from '../if-key-optional' describe('unit-d:types/IfOptionalKey', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsOptionalKey extends false', () => { - // Arrange - type K = 'last_name' + it('should equal F if IsOptionalKey extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) - // Expect - expectTypeOf>().toEqualTypeOf() + it('should equal T if IsOptionalKey extends true', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsOptionalKey extends true', () => { - // Arrange - type K = 'email' + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type U = Author | Book + type K = 'email' | 'isbn' | 'last_name' | 'publisher' - // Expect - expectTypeOf>().toEqualTypeOf() + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-key-readonly.spec-d.ts b/src/types/__tests__/if-key-readonly.spec-d.ts new file mode 100644 index 00000000..d2491fff --- /dev/null +++ b/src/types/__tests__/if-key-readonly.spec-d.ts @@ -0,0 +1,52 @@ +/** + * @file Type Tests - IfReadonlyKey + * @module tutils/types/tests/unit-d/IfReadonlyKey + */ + +import type Book from '#fixtures/book.interface' +import type Vehicle from '#fixtures/vehicle' +import type TestSubject from '../if-key-readonly' + +describe('unit-d:types/IfReadonlyKey', () => { + type F = 0 + type T = 1 + + it('should equal F if IsReadonlyKey extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsReadonlyKey extends true', () => { + // Arrange + type K = 'vin' + + // Expect + expectTypeOf, K, T, F>>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type U = Readonly | Readonly + type K = 'isbn' | 'vin' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-key-required.spec-d.ts b/src/types/__tests__/if-key-required.spec-d.ts index e5057223..23c8f9d8 100644 --- a/src/types/__tests__/if-key-required.spec-d.ts +++ b/src/types/__tests__/if-key-required.spec-d.ts @@ -4,25 +4,45 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' import type TestSubject from '../if-key-required' describe('unit-d:types/IfRequiredKey', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsRequiredKey extends false', () => { - // Arrange - type K = 'email' + it('should equal F if IsRequiredKey extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) - // Expect - expectTypeOf>().toEqualTypeOf() + it('should equal T if IsRequiredKey extends true', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsRequiredKey extends true', () => { - // Arrange - type K = 'first_name' + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type U = Author | Book + type K = 'email' | 'first_name' | 'isbn' | 'publisher' - // Expect - expectTypeOf>().toEqualTypeOf() + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-keys.spec-d.ts b/src/types/__tests__/if-keys.spec-d.ts new file mode 100644 index 00000000..5d617dfa --- /dev/null +++ b/src/types/__tests__/if-keys.spec-d.ts @@ -0,0 +1,31 @@ +/** + * @file Type Tests - IfKeys + * @module tutils/types/tests/unit-d/IfKeys + */ + +import type Vehicle from '#fixtures/vehicle' +import type TestSubject from '../if-keys' +import type Nilable from '../nilable' + +describe('unit-d:types/IfKeys', () => { + type F = 0 + type T = 1 + + it('should equal F if HasKeys extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if HasKeys extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf, T, F>>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-literal.spec-d.ts b/src/types/__tests__/if-literal.spec-d.ts new file mode 100644 index 00000000..1a6af37f --- /dev/null +++ b/src/types/__tests__/if-literal.spec-d.ts @@ -0,0 +1,34 @@ +/** + * @file Type Tests - IfLiteral + * @module tutils/types/tests/unit-d/IfLiteral + */ + +import type TestSubject from '../if-literal' + +describe('unit-d:types/IfLiteral', () => { + type F = 0 + type P = number + type T = 1 + + it('should equal F if IsLiteral extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsLiteral extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-negative.spec-d.ts b/src/types/__tests__/if-negative.spec-d.ts new file mode 100644 index 00000000..726b7f67 --- /dev/null +++ b/src/types/__tests__/if-negative.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IfNegative + * @module tutils/types/tests/unit-d/IfNegative + */ + +import type TestSubject from '../if-negative' + +describe('unit-d:types/IfNegative', () => { + type F = 0 + type T = 1 + + it('should equal F if IsNegative extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsNegative extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-never.spec-d.ts b/src/types/__tests__/if-never.spec-d.ts index fc0299a8..1234555c 100644 --- a/src/types/__tests__/if-never.spec-d.ts +++ b/src/types/__tests__/if-never.spec-d.ts @@ -6,14 +6,14 @@ import type TestSubject from '../if-never' describe('unit-d:types/IfNever', () => { - type False = false - type True = true + type False = 0 + type True = 1 - it('should equal False if IsNever extends false', () => { + it('should equal F if IsNever extends false', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsNever extends true', () => { + it('should equal T if IsNever extends true', () => { expectTypeOf>().toEqualTypeOf() }) }) diff --git a/src/types/__tests__/if-nil.spec-d.ts b/src/types/__tests__/if-nil.spec-d.ts index 139225ac..47c634f9 100644 --- a/src/types/__tests__/if-nil.spec-d.ts +++ b/src/types/__tests__/if-nil.spec-d.ts @@ -5,16 +5,31 @@ import type TestSubject from '../if-nil' import type NIL from '../nil' +import type Primitive from '../primitive' describe('unit-d:types/IfNil', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsNil extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsNil extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsNil extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsNil extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-null.spec-d.ts b/src/types/__tests__/if-null.spec-d.ts index cc3393ec..d6650658 100644 --- a/src/types/__tests__/if-null.spec-d.ts +++ b/src/types/__tests__/if-null.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-null' +import type Primitive from '../primitive' describe('unit-d:types/IfNull', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsNull extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsNull extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsNull extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsNull extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-number.spec-d.ts b/src/types/__tests__/if-number.spec-d.ts index ded78efe..6f8b4c54 100644 --- a/src/types/__tests__/if-number.spec-d.ts +++ b/src/types/__tests__/if-number.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-number' +import type Primitive from '../primitive' describe('unit-d:types/IfNumber', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsNumber extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsNumber extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsNumber extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsNumber extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-numeric-negative.spec-d.ts b/src/types/__tests__/if-numeric-negative.spec-d.ts new file mode 100644 index 00000000..0cdb7379 --- /dev/null +++ b/src/types/__tests__/if-numeric-negative.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IfNegativeNumeric + * @module tutils/types/tests/unit-d/IfNegativeNumeric + */ + +import type TestSubject from '../if-numeric-negative' + +describe('unit-d:types/IfNegativeNumeric', () => { + type F = 0 + type T = 1 + + it('should equal F if IsNegativeNumeric extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsNegativeNumeric extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-numeric.spec-d.ts b/src/types/__tests__/if-numeric.spec-d.ts index 5dbdcb27..9091ef81 100644 --- a/src/types/__tests__/if-numeric.spec-d.ts +++ b/src/types/__tests__/if-numeric.spec-d.ts @@ -4,17 +4,32 @@ */ import type TestSubject from '../if-numeric' +import type Nilable from '../nilable' import type Numeric from '../numeric' describe('unit-d:types/IfNumeric', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsNumeric extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsNumeric extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsNumeric extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsNumeric extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf, T, F>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-object-curly.spec-d.ts b/src/types/__tests__/if-object-curly.spec-d.ts index 3ad44836..9d0661f9 100644 --- a/src/types/__tests__/if-object-curly.spec-d.ts +++ b/src/types/__tests__/if-object-curly.spec-d.ts @@ -5,16 +5,31 @@ import type Book from '#fixtures/book.interface' import type TestSubject from '../if-object-curly' +import type Nilable from '../nilable' describe('unit-d:types/IfObjectCurly', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsObjectCurly extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsObjectCurly extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsObjectCurly extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsObjectCurly extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf, T, F>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-object-plain.spec-d.ts b/src/types/__tests__/if-object-plain.spec-d.ts index 3db501c6..a6cb429f 100644 --- a/src/types/__tests__/if-object-plain.spec-d.ts +++ b/src/types/__tests__/if-object-plain.spec-d.ts @@ -4,22 +4,33 @@ */ import type Book from '#fixtures/book.interface' +import type Vehicle from '#fixtures/vehicle' import type TestSubject from '../if-object-plain' -import type Simplify from '../simplify' +import type Nilable from '../nilable' describe('unit-d:types/IfObjectPlain', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsObjectPlain extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsObjectPlain extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsObjectPlain extends true', () => { - // Arrange - type T = Simplify + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsObjectPlain extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) - // Expect - expectTypeOf>().toEqualTypeOf() + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf, T, F>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-object.spec-d.ts b/src/types/__tests__/if-object.spec-d.ts index 5ba4ebf0..59ccc5eb 100644 --- a/src/types/__tests__/if-object.spec-d.ts +++ b/src/types/__tests__/if-object.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-object' +import type Nilable from '../nilable' describe('unit-d:types/IfObject', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsObject extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsObject extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsObject extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsObject extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf, T, F>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-primitive.spec-d.ts b/src/types/__tests__/if-primitive.spec-d.ts index 0bb22b6c..c0b55ac0 100644 --- a/src/types/__tests__/if-primitive.spec-d.ts +++ b/src/types/__tests__/if-primitive.spec-d.ts @@ -4,17 +4,36 @@ */ import type TestSubject from '../if-primitive' +import type OneOrMany from '../one-or-many' import type Primitive from '../primitive' describe('unit-d:types/IfPrimitive', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsPrimitive extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsPrimitive extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsPrimitive extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsPrimitive extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type U = OneOrMany + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-string.spec-d.ts b/src/types/__tests__/if-string.spec-d.ts index f0103629..ad874ff4 100644 --- a/src/types/__tests__/if-string.spec-d.ts +++ b/src/types/__tests__/if-string.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-string' +import type Primitive from '../primitive' describe('unit-d:types/IfString', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsString extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsString extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsString extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsString extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-symbol.spec-d.ts b/src/types/__tests__/if-symbol.spec-d.ts index 331bb417..3652d6e2 100644 --- a/src/types/__tests__/if-symbol.spec-d.ts +++ b/src/types/__tests__/if-symbol.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-symbol' +import type Primitive from '../primitive' describe('unit-d:types/IfSymbol', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsSymbol extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsSymbol extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsSymbol extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsSymbol extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-true.spec-d.ts b/src/types/__tests__/if-true.spec-d.ts new file mode 100644 index 00000000..62ca66e8 --- /dev/null +++ b/src/types/__tests__/if-true.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IfTrue + * @module tutils/types/tests/unit-d/IfTrue + */ + +import type TestSubject from '../if-true' + +describe('unit-d:types/IfTrue', () => { + type F = 0 + type T = 1 + + it('should equal F if IsTrue extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsTrue extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/if-tuple.spec-d.ts b/src/types/__tests__/if-tuple.spec-d.ts index 027a4200..7f3808b7 100644 --- a/src/types/__tests__/if-tuple.spec-d.ts +++ b/src/types/__tests__/if-tuple.spec-d.ts @@ -6,14 +6,32 @@ import type TestSubject from '../if-tuple' describe('unit-d:types/IfTuple', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsTuple extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsTuple extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsTuple extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsTuple extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type U = number | [0, 1, 2] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-undefined.spec-d.ts b/src/types/__tests__/if-undefined.spec-d.ts index d58c583c..40067504 100644 --- a/src/types/__tests__/if-undefined.spec-d.ts +++ b/src/types/__tests__/if-undefined.spec-d.ts @@ -4,16 +4,31 @@ */ import type TestSubject from '../if-undefined' +import type Primitive from '../primitive' describe('unit-d:types/IfUndefined', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsUndefined extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsUndefined extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsUndefined extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal F if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if IsUndefined extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/if-unknown.spec-d.ts b/src/types/__tests__/if-unknown.spec-d.ts index af3cffcf..ace6f6a3 100644 --- a/src/types/__tests__/if-unknown.spec-d.ts +++ b/src/types/__tests__/if-unknown.spec-d.ts @@ -6,14 +6,14 @@ import type TestSubject from '../if-unknown' describe('unit-d:types/IfUnknown', () => { - type False = false - type True = true + type F = 0 + type T = 1 - it('should equal False if IsUnknown extends false', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal F if IsUnknown extends false', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal True if IsUnknown extends true', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal T if IsUnknown extends true', () => { + expectTypeOf>().toEqualTypeOf() }) }) diff --git a/src/types/__tests__/indices.spec-d.ts b/src/types/__tests__/indices.spec-d.ts index 2665c20a..6a836818 100644 --- a/src/types/__tests__/indices.spec-d.ts +++ b/src/types/__tests__/indices.spec-d.ts @@ -4,23 +4,77 @@ */ import type EmptyArray from '../empty-array' +import type EmptyString from '../empty-string' import type TestSubject from '../indices' -import type Numeric from '../numeric' +import type Length from '../length' +import type NaturalRange from '../range-natural' +import type Subtract from '../subtract' +import type UnwrapNumeric from '../unwrap-numeric' describe('unit-d:types/Indices', () => { - it('should equal Numeric | number if IsTuple extends false', () => { - expectTypeOf>().toEqualTypeOf() + type IndicesRange = + Length extends infer L extends number + ? NaturalRange extends infer R extends number + ? { + [K in R]: + | K + | UnwrapNumeric< + Exclude<`-${Subtract>, K>}`, '-0'> + > + }[R] + : never + : never + + it('should equal number if T is any', () => { + expectTypeOf>().toBeNumber() }) - it('should equal indices union if T if IsTuple extends true', () => { - expectTypeOf>().toEqualTypeOf<'0' | '1'>() + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() }) - it('should equal never if T extends EmptyArray', () => { - expectTypeOf>().toBeNever() + describe('T extends string', () => { + it('should equal never if T extends EmptyString', () => { + expectTypeOf>().toBeNever() + }) + + describe('Split extends readonly [T[0], ...T[number][]]', () => { + it('should construct union of negative and positive indices', () => { + // Arrange + type T = 'abc' + + // Expect + expectTypeOf>().toEqualTypeOf>() + }) + }) + + describe('number extends Length', () => { + it('should equal number', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) }) - it('should equal never if T is not an array', () => { - expectTypeOf>().toBeNever() + describe('T extends readonly unknown[]', () => { + it('should equal never if T extends Readonly', () => { + expectTypeOf>().toBeNever() + expectTypeOf>>().toBeNever() + }) + + describe('T extends readonly [T[0], ...T[number][]]', () => { + it('should construct union of negative and positive indices', () => { + // Arrange + type T = ['a', 'b'?] + + // Expect + expectTypeOf>().toEqualTypeOf>() + }) + }) + + describe('number extends Length', () => { + it('should equal number', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) }) }) diff --git a/src/types/__tests__/intersection.spec-d.ts b/src/types/__tests__/intersection.spec-d.ts new file mode 100644 index 00000000..e8a6e5a7 --- /dev/null +++ b/src/types/__tests__/intersection.spec-d.ts @@ -0,0 +1,23 @@ +/** + * @file Type Tests - Intersection + * @module tutils/types/tests/unit-d/Intersection + */ + +import type Vehicle from '#fixtures/vehicle' +import type Dot from '../dot' +import type Indices from '../indices' +import type TestSubject from '../intersection' +import type Join from '../join' + +describe('unit-d:types/Intersection', () => { + type V = [Vehicle, Vehicle] + + it('should equal intersection of T and U', () => { + // Arrange + type T = Indices | Join<[Indices, keyof Vehicle], Dot> + type U = '1.vin' | 'data' | 0 + + // Expect + expectTypeOf>().toEqualTypeOf>() + }) +}) diff --git a/src/types/__tests__/is-array.spec-d.ts b/src/types/__tests__/is-array.spec-d.ts index e5d0777c..97466dd0 100644 --- a/src/types/__tests__/is-array.spec-d.ts +++ b/src/types/__tests__/is-array.spec-d.ts @@ -4,12 +4,11 @@ */ import type TestSubject from '../is-array' -import type Nullable from '../nullable' +import type Nilable from '../nilable' describe('unit-d:types/IsArray', () => { - it('should equal false if [T] does not extend [readonly V[]]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend readonly V[]', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -20,8 +19,15 @@ describe('unit-d:types/IsArray', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [readonly V[]]', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal true if T extends readonly V[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-big-int.spec-d.ts b/src/types/__tests__/is-big-int.spec-d.ts index 514c71dc..38fe64a0 100644 --- a/src/types/__tests__/is-big-int.spec-d.ts +++ b/src/types/__tests__/is-big-int.spec-d.ts @@ -4,12 +4,11 @@ */ import type TestSubject from '../is-big-int' -import type Nullable from '../nullable' +import type Nilable from '../nilable' describe('unit-d:types/IsBigInt', () => { - it('should equal false if [T] does not extend [bigint]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend bigint', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -20,7 +19,15 @@ describe('unit-d:types/IsBigInt', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [bigint]', () => { + it('should equal true if T extends bigint', () => { + expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + }) + }) }) diff --git a/src/types/__tests__/is-boolean.spec-d.ts b/src/types/__tests__/is-boolean.spec-d.ts index 53ed624a..ae180c6e 100644 --- a/src/types/__tests__/is-boolean.spec-d.ts +++ b/src/types/__tests__/is-boolean.spec-d.ts @@ -7,8 +7,8 @@ import type Booleanish from '../booleanish' import type TestSubject from '../is-boolean' describe('unit-d:types/IsBoolean', () => { - it('should equal false if [T] does not extend [boolean]', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend boolean', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -19,7 +19,15 @@ describe('unit-d:types/IsBoolean', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [boolean]', () => { + it('should equal true if T extends boolean', () => { expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-equal.spec-d.ts b/src/types/__tests__/is-equal.spec-d.ts index df773129..4d08cf85 100644 --- a/src/types/__tests__/is-equal.spec-d.ts +++ b/src/types/__tests__/is-equal.spec-d.ts @@ -7,12 +7,13 @@ import type TestSubject from '../is-equal' describe('unit-d:types/IsEqual', () => { it('should equal false A and B are not equal', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal true if A and B are equal', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) }) diff --git a/src/types/__tests__/is-false.spec-d.ts b/src/types/__tests__/is-false.spec-d.ts new file mode 100644 index 00000000..2521ce9a --- /dev/null +++ b/src/types/__tests__/is-false.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IsFalse + * @module tutils/types/tests/unit-d/IsFalse + */ + +import type Falsy from '../falsy' +import type TestSubject from '../is-false' + +describe('unit-d:types/IsFalse', () => { + it('should equal false if T does not extend false', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends false', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-function.spec-d.ts b/src/types/__tests__/is-function.spec-d.ts index 3f9407a7..12ecc9f5 100644 --- a/src/types/__tests__/is-function.spec-d.ts +++ b/src/types/__tests__/is-function.spec-d.ts @@ -5,12 +5,11 @@ import type Fn from '../fn' import type TestSubject from '../is-function' -import type Optional from '../optional' +import type Nilable from '../nilable' describe('unit-d:types/IsFunction', () => { - it('should equal false if [T] does not extend [Fn]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend Function', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -21,7 +20,15 @@ describe('unit-d:types/IsFunction', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [Fn]', () => { + it('should equal true if T extends Function', () => { + expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() + expectTypeOf string>>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-index-signature.spec-d.ts b/src/types/__tests__/is-index-signature.spec-d.ts new file mode 100644 index 00000000..e5543349 --- /dev/null +++ b/src/types/__tests__/is-index-signature.spec-d.ts @@ -0,0 +1,195 @@ +/** + * @file Type Tests - IsIndexSignature + * @module tutils/types/tests/unit-d/IsIndexSignature + */ + +import type Fn from '../fn' +import type TestSubject from '../is-index-signature' +import type Nilable from '../nilable' +import type Numeric from '../numeric' +import type OneOrMany from '../one-or-many' + +describe('unit-d:types/IsIndexSignature', () => { + it('should equal false if K is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is unknown', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends ObjectCurly', () => { + type T = { + [x: Numeric]: string + [x: number]: any + [x: string]: any + [x: symbol]: any + hello: 'world' + foo?: string + } + + it('should equal false if K is not index signature key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends Primitive', () => { + describe('T extends bigint', () => { + type T = bigint & { + [x: Numeric]: string + [x: number]: any + [x: string]: any + [x: symbol]: any + } + + it('should equal false if K is not index signature key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends boolean', () => { + type T = boolean & { + [x: Numeric]: string + [x: number]: any + [x: string]: any + [x: symbol]: any + } + + it('should equal false if K is not index signature key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends number', () => { + type T = number & { + [x: Numeric]: string + [x: number]: any + [x: string]: any + [x: symbol]: any + } + + it('should equal false if K is not index signature key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends string', () => { + type T = string + + it('should equal false if K is not index signature key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends symbol', () => { + type T = symbol & { + [x: Numeric]: string + [x: number]: any + [x: string]: any + [x: symbol]: any + } + + it('should equal false if K is not index signature key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Readonly', () => { + type T = Readonly & { + [x: Numeric]: string + [x: number]: any + [x: string]: any + [x: symbol]: any + } + + it('should equal false if K is not index signature key', () => { + expectTypeOf>>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends readonly unknown[]', () => { + type T = string[] + + it('should equal false if K is not index signature key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is index signature key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable | { [x: Numeric]: number }> + type K = Numeric | number + + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-integer-negative.spec-d.ts b/src/types/__tests__/is-integer-negative.spec-d.ts new file mode 100644 index 00000000..7b1cc59e --- /dev/null +++ b/src/types/__tests__/is-integer-negative.spec-d.ts @@ -0,0 +1,31 @@ +/** + * @file Type Tests - IsNegativeInteger + * @module tutils/types/tests/unit-d/IsNegativeInteger + */ + +import type TestSubject from '../is-integer-negative' + +describe('unit-d:types/IsNegativeInteger', () => { + it('should equal false if T does not extend a negative integer', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends a negative integer', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-integer.spec-d.ts b/src/types/__tests__/is-integer.spec-d.ts new file mode 100644 index 00000000..ccfb05e9 --- /dev/null +++ b/src/types/__tests__/is-integer.spec-d.ts @@ -0,0 +1,34 @@ +/** + * @file Type Tests - IsInteger + * @module tutils/types/tests/unit-d/IsInteger + */ + +import type Integer from '../integer' +import type TestSubject from '../is-integer' + +describe('unit-d:types/IsInteger', () => { + it('should equal false if T does not extend an integer', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends an integer', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-json-primitive.spec-d.ts b/src/types/__tests__/is-json-primitive.spec-d.ts index 95acfeb4..f4f12aa5 100644 --- a/src/types/__tests__/is-json-primitive.spec-d.ts +++ b/src/types/__tests__/is-json-primitive.spec-d.ts @@ -5,12 +5,12 @@ import type TestSubject from '../is-json-primitive' import type JsonPrimitive from '../json-primitive' -import type OneOrMany from '../one-or-many' +import type Optional from '../optional' describe('unit-d:types/IsJsonPrimitive', () => { - it('should equal false if [T] does not extend [JsonPrimitive]', () => { - expectTypeOf>>().toEqualTypeOf() + it('should equal false if T does not extend JsonPrimitive', () => { expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -21,7 +21,22 @@ describe('unit-d:types/IsJsonPrimitive', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [JsonPrimitive]', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal true if T extends JsonPrimitive', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T1 = JsonPrimitive + type T2 = Optional + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-key-optional-exact.spec-d.ts b/src/types/__tests__/is-key-optional-exact.spec-d.ts index 306bae11..3645a7b1 100644 --- a/src/types/__tests__/is-key-optional-exact.spec-d.ts +++ b/src/types/__tests__/is-key-optional-exact.spec-d.ts @@ -4,46 +4,407 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' +import type EmptyString from '../empty-string' +import type Fn from '../fn' +import type Indices from '../indices' import type TestSubject from '../is-key-optional-exact' -import type Join from '../join' +import type Numeric from '../numeric' +import type PropertyKey from '../property-key' +import type Stringify from '../stringify' describe('unit-d:types/IsExactOptionalKey', () => { - type T1 = Author - type T2 = { data: { author: Author } } - it('should equal false if K is any', () => { - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal false if K is never', () => { - expectTypeOf>().toEqualTypeOf() - }) - - it('should equal false if K is not exact optional property of T', () => { - // Arrange - type K1 = 'display_name' - type K2 = 'first_name' - type K3 = K1 | K2 | 'email' - type K4 = Join<['data', 'author', K1]> - type K5 = Join<['data', 'author', K2]> - type K6 = Join<['data', 'author', 'email']> | K4 | K5 - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - }) - - it('should equal true if K is exact optional property of T', () => { - // Arrange - type K1 = 'email' - type K2 = Join<['data', 'author', K1]> - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is unknown', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends ObjectCurly', () => { + it('should equal false if keyof T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + type T = Book + + it('should equal false if K is not nested exact optional key', () => { + // Arrange + type K = 'publisher.display_name.value' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + // Arrange + type K = `authors.${Numeric}.email` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Author + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Primitive', () => { + it('should equal false if keyof T is never', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends bigint', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0n & { x: { a: string; b?: string; c?: string | undefined } } + + it('should equal false if K is not nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1n & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends boolean', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = false & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = true & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends number', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0 & { x: { a: string; b?: string; c?: string | undefined } } + + it('should equal false if K is not nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1 & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + describe('IsLiteral extends true', () => { + it('should equal false given any K', () => { + // Arrange + type T = 'xyz' + + // Expect + expectTypeOf>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends Indices', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = string & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = string & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends symbol', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = symbol & { x: { b?: string; c?: string | undefined } } + + it('should equal false if K is not nested exact optional key', () => { + // Arrange + type K = 'description.length' | 'x.c' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = symbol & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends Readonly', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = Readonly & { x: { b?: string; c?: string | undefined } } + + it('should equal false if K is not nested exact optional key', () => { + // Arrange + type K = 'length.toFixed' | 'x.c' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Readonly & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + type T = readonly [Author, Author?, Author?] + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal false if K is not nested exact optional key', () => { + // Arrange + type K1 = `${Indices}.${Exclude}` + type K2 = `${Numeric}.${Exclude}` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + // Arrange + type K1 = `${Indices}.email` + type K2 = `${Numeric}.email` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Indices', () => { + it('should equal false if K is not exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + // Arrange + type K = Exclude, -3 | 0> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Stringify>', () => { + it('should equal false if K is not exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + // Arrange + type K = Exclude>, '-3' | '0'> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = readonly [Author?] & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('number extends Length', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = Author[] + + it('should equal false if K is not nested exact optional key', () => { + // Arrange + type K = `${Numeric}.${Exclude}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested exact optional key', () => { + // Arrange + type K = `${Numeric}.email` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Author[] & { b?: string; c?: string | undefined } + + it('should equal false if K is not exact optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is exact optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type K = `${EmptyString | 'authors.0.'}email` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-key-optional.spec-d.ts b/src/types/__tests__/is-key-optional.spec-d.ts index e2cb148f..3eab7989 100644 --- a/src/types/__tests__/is-key-optional.spec-d.ts +++ b/src/types/__tests__/is-key-optional.spec-d.ts @@ -4,54 +4,421 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' +import type Vehicle from '#fixtures/vehicle' +import type EmptyString from '../empty-string' +import type Fn from '../fn' +import type Indices from '../indices' import type TestSubject from '../is-key-optional' -import type Join from '../join' +import type Nilable from '../nilable' +import type Numeric from '../numeric' +import type PropertyKey from '../property-key' +import type Stringify from '../stringify' describe('unit-d:types/IsOptionalKey', () => { - type T1 = Author - type T2 = { data: { author: Author } } - it('should equal false if K is any', () => { - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal false if K is never', () => { - expectTypeOf>().toEqualTypeOf() - }) - - it('should equal false if K is not optional property of T', () => { - // Arrange - type K1 = 'first_name' - type K2 = 'last_name' - type K3 = K1 | K2 | 'display_name' - type K4 = Join<['data', 'author', K1]> - type K5 = Join<['data', 'author', K2]> - type K6 = Join<['data', 'author', 'email']> | K4 | K5 - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - }) - - it('should equal true if K is optional property of T', () => { - // Arrange - type K1 = 'display_name' - type K2 = 'email' - type K3 = K1 | K2 - type K4 = Join<['data', 'author', K1]> - type K5 = Join<['data', 'author', K2]> - type K6 = K4 | K5 - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is unknown', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends ObjectCurly', () => { + it('should equal false if keyof T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + type T = Book + + it('should equal false if K is not nested optional key', () => { + // Arrange + type K = 'publisher.display_name.value' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + // Arrange + type K = `authors.${Numeric}.${'display_name' | 'email'}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Author + + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + // Arrange + type K = Extract + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Primitive', () => { + it('should equal false if keyof T is never', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends bigint', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0n & { x: { a: string; b?: string; c?: string | undefined } } + + it('should equal false if K is not nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1n & { b?: string; c?: string | undefined } + + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends boolean', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = false & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = true & { b?: string; c?: string | undefined } + + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends number', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0 & { x: { a: string; b?: string; c?: string | undefined } } + + it('should equal false if K is not nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1 & { b?: string; c?: string | undefined } + + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + describe('IsLiteral extends true', () => { + it('should equal false given any K', () => { + // Arrange + type T = 'xyz' + + // Expect + expectTypeOf>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends Indices', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = string & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested optional key', () => { + // Arrange + type K = 'length.toFixed' | 'x.a' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = string & { b?: string; c?: string | undefined } + + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends symbol', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = symbol & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested optional key', () => { + // Arrange + type K = 'description.length' | 'x.a' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = symbol & { b?: string; c?: string | undefined } + + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends Readonly', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = Readonly & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested optional key', () => { + // Arrange + type K = 'length.toFixed' | 'x.a' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Readonly & { b?: string; c?: string | undefined } + + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + type T = readonly [Author, Nilable, Author?] + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal false if K is not nested optional key', () => { + // Arrange + type K1 = `${Indices}.${'first_name' | 'last_name'}` + type K2 = `${Numeric}.${'first_name' | 'last_name'}` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + // Arrange + type K1 = `${Indices}.${'display_name' | 'email'}` + type K2 = `${Numeric}.${'display_name' | 'email'}` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Indices', () => { + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude, -1 | 2> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + it('should equal false if K is not optional key', () => { + // Arrange + type T = readonly [Author] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + // Arrange + type T = readonly [Author] & { b?: string; c?: string | undefined } + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Stringify>', () => { + it('should equal false if K is not optional key', () => { + // Arrange + type K = Exclude>, '-1' | '2'> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('number extends Indices', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type K = `${Numeric}.${keyof Vehicle}` + + it('should equal false if K is not nested optional key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested optional key', () => { + // Arrange + type T = readonly Partial[] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + it('should equal false if K is not optional key', () => { + // Arrange + type T = Partial + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is optional key', () => { + // Arrange + type T = Partial & { b?: string; c?: string | undefined } + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type K = `${EmptyString | 'authors.0.'}${'display_name' | 'email'}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-key-readonly.spec-d.ts b/src/types/__tests__/is-key-readonly.spec-d.ts new file mode 100644 index 00000000..b972817f --- /dev/null +++ b/src/types/__tests__/is-key-readonly.spec-d.ts @@ -0,0 +1,571 @@ +/** + * @file Type Tests - IsReadonlyKey + * @module tutils/types/tests/unit-d/IsReadonlyKey + */ + +import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' +import type Vehicle from '#fixtures/vehicle' +import type Fn from '../fn' +import type Indices from '../indices' +import type TestSubject from '../is-key-readonly' +import type Nilable from '../nilable' +import type Numeric from '../numeric' +import type { tag } from '../opaque' +import type Optional from '../optional' +import type Stringify from '../stringify' + +describe('unit-d:types/IsReadonlyKey', () => { + it('should equal false if K is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if K is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is unknown', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends ObjectCurly', () => { + it('should equal false if keyof T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + type K1 = 'data.-1.author.display_name' + type K2 = 'data.-1.author.email' + type K3 = 'data.-1.author.first_name' + + it('should equal false if K is not nested readonly key', () => { + // Arrange + type T = { data: { [-1]: { author: Author } } } + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type T = { data: { [-1]: { author: Optional> } } } + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + it('should equal false if K is not readonly key', () => { + // Arrange + type T = Author & { [tag]: 'author' } + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type T = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Primitive', () => { + it('should equal false if keyof T is never', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends bigint', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0n & { + x: { + readonly a: string + readonly b?: string + readonly c?: string | undefined + d: string + e?: string + } + } + + it('should equal false if K is not nested readonly key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type K1 = `x.${Exclude}` + type K2 = `x.${keyof T['x']}.length` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1n & { + readonly a: string + readonly b?: string + readonly c?: string | undefined + } + + it('should equal false if K is not readonly key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type K = Extract + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends boolean', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = false & { + x: { + readonly a: string + readonly b?: string + readonly c?: string | undefined + d: string + e?: string + } + } + + it('should equal false if K is not nested readonly key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type K1 = `x.${Exclude}` + type K2 = `x.${keyof T['x']}.length` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = true & { + readonly a: string + readonly b?: string + readonly c?: string | undefined + } + + it('should equal false if K is not readonly key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type K = Extract + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends number', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0 & { + x: { + readonly a: string + readonly b?: string + readonly c?: string | undefined + d: string + e?: string + } + } + + it('should equal false if K is not nested readonly key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type K1 = `x.${Exclude}` + type K2 = `x.${keyof T['x']}.length` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1 & { + readonly a: string + readonly b?: string + readonly c?: string | undefined + } + + it('should equal false if K is not readonly key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type K = Extract + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + describe('IsLiteral extends true', () => { + type T = 'xyz' + + describe('K extends keyof T', () => { + it('should equal false if K is not readonly key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type K = Extract + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('number extends Length', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = string & { + x: { + readonly a: string + readonly b?: string + readonly c?: string | undefined + d: string + e?: string + } + } + + it('should equal false if K is not nested readonly key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type K1 = `x.${Exclude}` + type K2 = `x.${keyof T['x']}.length` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = string & { + readonly a: string + readonly b?: string + readonly c?: string | undefined + } + + it('should equal false if K is not readonly key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type K = Extract + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends symbol', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = symbol & { + x: { + readonly a: string + readonly b?: string + readonly c?: string | undefined + d: string + e?: string + } + } + + it('should equal false if K is not nested readonly key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type K1 = 'description.length' + type K2 = `x.${Exclude}` + type K3 = `x.${keyof T['x']}.length` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = symbol & { + readonly a: string + readonly b?: string + readonly c?: string | undefined + } + + it('should equal false if K is not readonly key', () => { + // Arrange + type K = Exclude< + keyof T, + typeof Symbol.toStringTag | 'a' | 'b' | 'c' | 'description' + > + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type K = Extract< + keyof T, + typeof Symbol.toStringTag | 'a' | 'b' | 'c' | 'description' + > + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends Readonly', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = Readonly & { + x: { + readonly a: string + readonly b?: string + readonly c?: string | undefined + d: string + e?: string + } + } + + it('should equal false if K is not nested readonly key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type K1 = `x.${Exclude}` + type K2 = `x.${keyof T['x']}.length` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + it('should equal false if K is not readonly key', () => { + // Arrange + type T = Fn + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type T1 = Fn + type T2 = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = [Vehicle, Readonly?] + + it('should equal false if K is not nested readonly key', () => { + // Arrange + type K = `${Stringify<-2 | 0>}.${keyof Vehicle}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type K = `${Stringify<-1 | 1>}.${keyof Vehicle}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Indices', () => { + it('should equal false if K is not readonly key', () => { + // Arrange + type T = [Vehicle, Nilable, Vehicle?] + + // Expect + expectTypeOf>>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type T = readonly [Vehicle, Nilable, Vehicle?] + + // Expect + expectTypeOf>>().toEqualTypeOf() + }) + }) + + describe('K extends Stringify>', () => { + it('should equal false if K is not readonly key', () => { + // Arrange + type T = [Vehicle, Nilable, Vehicle?] + type K = Stringify> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type T = readonly [Vehicle, Nilable, Vehicle?] + type K = Stringify> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + it('should equal false if K is not readonly key', () => { + // Arrange + type T = [Vehicle, Vehicle?] + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type T = readonly [Vehicle, Vehicle?] + type K = Stringify> | typeof Symbol.unscopables | 'length' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('number extends Length', () => { + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal false if K is not nested readonly key', () => { + // Arrange + type T = Vehicle[] + type K = `${Numeric}.${keyof Vehicle}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested readonly key', () => { + // Arrange + type T = Readonly[] + type K = `${Numeric}.${keyof Vehicle}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Vehicle[] + + it('should equal false if K is not readonly key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is readonly key', () => { + // Arrange + type K = Extract + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Readonly | Readonly + type K = 'isbn' | 'vin' + + // 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 index c5cb01e7..5a9f35fd 100644 --- a/src/types/__tests__/is-key-required.spec-d.ts +++ b/src/types/__tests__/is-key-required.spec-d.ts @@ -4,54 +4,426 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' +import type Vehicle from '#fixtures/vehicle' +import type EmptyString from '../empty-string' +import type Fn from '../fn' +import type Indices from '../indices' import type TestSubject from '../is-key-required' -import type Join from '../join' +import type Nilable from '../nilable' +import type Numeric from '../numeric' +import type Stringify from '../stringify' describe('unit-d:types/IsRequiredKey', () => { - type T1 = Author - type T2 = { data: { author: Author } } - it('should equal false if K is any', () => { - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal false if K is never', () => { - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal false if K is not required property of T', () => { - // Arrange - type K1 = 'display_name' - type K2 = 'email' - type K3 = K1 | K2 | 'first_name' - type K4 = Join<['data', 'author', K1]> - type K5 = Join<['data', 'author', K2]> - type K6 = Join<['data', 'author', 'last_name']> | K4 | K5 - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal true if K is required property of T', () => { - // Arrange - type K1 = 'first_name' - type K2 = 'last_name' - type K3 = K1 | K2 - type K4 = Join<['data', 'author', K1]> - type K5 = Join<['data', 'author', K2]> - type K6 = K4 | K5 - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T is unknown', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends ObjectCurly', () => { + it('should equal false if keyof T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('K extends `${infer H}.${infer R}`', () => { + type T = Book + + it('should equal false if K is not nested required key', () => { + // Arrange + type K = `authors.${Numeric}.${'display_name' | 'email'}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + // Arrange + type K = 'publisher.display_name.value' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Author + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Primitive', () => { + it('should equal false if Keyof is never', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends bigint', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0n & { x: { a: string; b?: string; c?: string | undefined } } + + it('should equal false if K is not nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1n & { b?: string; c?: string | undefined } + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends boolean', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = false & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = true & { b?: string; c?: string | undefined } + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends number', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = 0 & { x: { a: string; b?: string; c?: string | undefined } } + + it('should equal false if K is not nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = 1 & { b?: string; c?: string | undefined } + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + describe('IsLiteral extends true', () => { + type T = 'xyz' + + describe('K extends Indices', () => { + it('should equal true', () => { + // Arrange + type K = Indices + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Stringify>', () => { + it('should equal true', () => { + // Arrange + type K = Stringify> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + it('should equal true', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('number extends Indices', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = string & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + // Arrange + type K = 'length.toFixed' | 'x.a' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = string & { b?: string; c?: string | undefined } + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends symbol', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = symbol & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + // Arrange + type K = 'description.length' | 'x.a' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = symbol & { b?: string; c?: string | undefined } + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Readonly', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type T = Readonly & { + x: { a: string; b?: string; c?: string | undefined } + } + + it('should equal false if K is not nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + // Arrange + type K = 'length.toFixed' | 'x.a' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Readonly & { b?: string; c?: string | undefined } + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + type T = readonly [Vehicle, Nilable, Partial?] + + describe('K extends `${infer H}.${infer R}`', () => { + it('should equal false if K is not nested required key', () => { + // Arrange + type K = `${Extract, -1 | 2>}.${keyof Vehicle}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + // Arrange + type K = `${Exclude, -1 | 2>}.${keyof Vehicle}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Indices', () => { + it('should equal false if K is not required key', () => { + // Arrange + type K = Extract, -1 | 2> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude, -1 | 2> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends Stringify>', () => { + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude>, '-1' | '2'> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('number extends Indices', () => { + describe('K extends `${infer H}.${infer R}`', () => { + type K = `${Numeric}.${keyof Vehicle}` + + it('should equal false if K is not nested required key', () => { + // Arrange + type T = Partial[] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is nested required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('K extends keyof T', () => { + type T = Vehicle[] & { b?: string; c?: string | undefined } + + it('should equal false if K is not required key', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if K is required key', () => { + // Arrange + type K = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type K = `${EmptyString | 'authors.0.'}${'first_name' | 'last_name'}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) }) }) diff --git a/src/types/__tests__/is-literal.spec-d.ts b/src/types/__tests__/is-literal.spec-d.ts new file mode 100644 index 00000000..02f0787b --- /dev/null +++ b/src/types/__tests__/is-literal.spec-d.ts @@ -0,0 +1,143 @@ +/** + * @file Type Tests - IsLiteral + * @module tutils/types/tests/unit-d/IsLiteral + */ + +import type Booleanish from '../booleanish' +import type TestSubject from '../is-literal' +import type { tag } from '../opaque' +import type Primitive from '../primitive' + +describe('unit-d:types/IsLiteral', () => { + type ToObject

= P & { readonly [tag]: 'primitive' } + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is unknown', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('P extends bigint', () => { + type P = bigint + + it('should equal false if T does not extend literal bigint', () => { + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends literal bigint', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('P extends boolean', () => { + type P = boolean + + it('should equal false if T does not extend literal boolean', () => { + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends literal boolean', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('P extends null', () => { + type P = null + + it('should equal false if T does not extend null', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends null', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('P extends number', () => { + type P = number + + it('should equal false if T does not extend literal number', () => { + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends literal number', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('P extends string', () => { + type P = string + + it('should equal false if T does not extend literal string', () => { + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends literal string', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('P extends symbol', () => { + type P = symbol + + it('should equal false given any T', () => { + expectTypeOf>>().toEqualTypeOf() + expectTypeOf, P>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('P extends undefined', () => { + type P = undefined + + it('should equal false if T does not extend undefined', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends undefined', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-negative.spec-d.ts b/src/types/__tests__/is-negative.spec-d.ts new file mode 100644 index 00000000..86c655d2 --- /dev/null +++ b/src/types/__tests__/is-negative.spec-d.ts @@ -0,0 +1,38 @@ +/** + * @file Type Tests - IsNegative + * @module tutils/types/tests/unit-d/IsNegative + */ + +import type TestSubject from '../is-negative' + +describe('unit-d:types/IsNegative', () => { + it('should equal false if T does not extend negative value', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends NegativeNumeric', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends negative bigint', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends negative integer', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-nil.spec-d.ts b/src/types/__tests__/is-nil.spec-d.ts index cfcf389f..9df607a6 100644 --- a/src/types/__tests__/is-nil.spec-d.ts +++ b/src/types/__tests__/is-nil.spec-d.ts @@ -7,9 +7,8 @@ import type TestSubject from '../is-nil' import type NIL from '../nil' describe('unit-d:types/IsNil', () => { - it('should equal false if [T] does not extend [NIL]', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend NIL', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -20,7 +19,15 @@ describe('unit-d:types/IsNil', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [NIL]', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal true if T extends NIL', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-null.spec-d.ts b/src/types/__tests__/is-null.spec-d.ts index b69862e8..03922686 100644 --- a/src/types/__tests__/is-null.spec-d.ts +++ b/src/types/__tests__/is-null.spec-d.ts @@ -7,9 +7,9 @@ import type TestSubject from '../is-null' import type Nullable from '../nullable' describe('unit-d:types/IsNull', () => { - it('should equal false if [T] does not extend [null]', () => { - expectTypeOf>>().toEqualTypeOf() + it('should equal false if T does not extend null', () => { expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -20,7 +20,13 @@ describe('unit-d:types/IsNull', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [null]', () => { + it('should equal true if T extends null', () => { expectTypeOf>().toEqualTypeOf() }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) + }) }) diff --git a/src/types/__tests__/is-number.spec-d.ts b/src/types/__tests__/is-number.spec-d.ts index e5213172..fc82800b 100644 --- a/src/types/__tests__/is-number.spec-d.ts +++ b/src/types/__tests__/is-number.spec-d.ts @@ -3,14 +3,15 @@ * @module tutils/types/tests/unit-d/IsNumber */ +import type Float from '../float' +import type Integer from '../integer' import type TestSubject from '../is-number' -import type Nullable from '../nullable' +import type Nilable from '../nilable' import type Timestamp from '../timestamp' describe('unit-d:types/IsNumber', () => { - it('should equal false if [T] does not extend [number]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend number', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -21,8 +22,16 @@ describe('unit-d:types/IsNumber', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [number]', () => { + it('should equal true if T extends number', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() expectTypeOf>>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) + }) }) diff --git a/src/types/__tests__/is-numeric-negative.spec-d.ts b/src/types/__tests__/is-numeric-negative.spec-d.ts new file mode 100644 index 00000000..3dd7891f --- /dev/null +++ b/src/types/__tests__/is-numeric-negative.spec-d.ts @@ -0,0 +1,41 @@ +/** + * @file Type Tests - IsNegativeNumeric + * @module tutils/types/tests/unit-d/IsNegativeNumeric + */ + +import type TestSubject from '../is-numeric-negative' +import type Numeric from '../numeric' +import type NegativeNumeric from '../numeric-negative' + +describe('unit-d:types/IsNegativeNumeric', () => { + it('should equal false if T does not extend NegativeNumeric', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if Trim does not extend T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends NegativeNumeric', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = NegativeNumeric | Numeric + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-numeric.spec-d.ts b/src/types/__tests__/is-numeric.spec-d.ts index a03caf47..fc34b863 100644 --- a/src/types/__tests__/is-numeric.spec-d.ts +++ b/src/types/__tests__/is-numeric.spec-d.ts @@ -6,14 +6,14 @@ import type TestSubject from '../is-numeric' import type Nilable from '../nilable' import type Numeric from '../numeric' +import type NegativeNumeric from '../numeric-negative' describe('unit-d:types/IsNumeric', () => { - it('should equal false if [T] does not extend [Numeric]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend Numeric', () => { + expectTypeOf>().toEqualTypeOf() }) - it('should equal false if [Trim] does not extend [T]', () => { + it('should equal false if Trim does not extend T', () => { expectTypeOf>().toEqualTypeOf() }) @@ -25,7 +25,18 @@ describe('unit-d:types/IsNumeric', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [Numeric]', () => { + it('should equal true if NegativeNumeric extends T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends Numeric', () => { + expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) + }) }) diff --git a/src/types/__tests__/is-object-curly.spec-d.ts b/src/types/__tests__/is-object-curly.spec-d.ts index 9b7a0a86..07071dfb 100644 --- a/src/types/__tests__/is-object-curly.spec-d.ts +++ b/src/types/__tests__/is-object-curly.spec-d.ts @@ -4,18 +4,14 @@ */ import type Book from '#fixtures/book.interface' -import type Fn from '../fn' +import type Vehicle from '#fixtures/vehicle' +import type EmptyObject from '../empty-object' import type TestSubject from '../is-object-curly' -import type ObjectCurly from '../object-curly' -import type ObjectPlain from '../object-plain' -import type Primitive from '../primitive' -import type Simplify from '../simplify' +import type Nilable from '../nilable' describe('unit-d:types/IsObjectCurly', () => { - it('should equal false if [T] does not extend [ObjectCurly]', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend ObjectCurly', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -26,11 +22,15 @@ describe('unit-d:types/IsObjectCurly', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [ObjectCurly]', () => { + it('should equal true if T extends ObjectCurly', () => { expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-object-plain.spec-d.ts b/src/types/__tests__/is-object-plain.spec-d.ts index b6189c62..ea5efcd9 100644 --- a/src/types/__tests__/is-object-plain.spec-d.ts +++ b/src/types/__tests__/is-object-plain.spec-d.ts @@ -3,19 +3,15 @@ * @module tutils/types/tests/unit-d/IsObjectPlain */ -import type Book from '#fixtures/book.interface' -import type Fn from '../fn' +import type Vehicle from '#fixtures/vehicle' +import type EmptyObject from '../empty-object' import type TestSubject from '../is-object-plain' +import type Nilable from '../nilable' import type ObjectPlain from '../object-plain' -import type Primitive from '../primitive' -import type Simplify from '../simplify' describe('unit-d:types/IsObjectPlain', () => { - it('should equal false if [T] does not extend [ObjectPlain]', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend ObjectPlain', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -26,8 +22,15 @@ describe('unit-d:types/IsObjectPlain', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [ObjectPlain]', () => { + it('should equal true if T extends ObjectPlain', () => { + expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() - expectTypeOf>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-object.spec-d.ts b/src/types/__tests__/is-object.spec-d.ts index 3f1654ac..701e736f 100644 --- a/src/types/__tests__/is-object.spec-d.ts +++ b/src/types/__tests__/is-object.spec-d.ts @@ -3,15 +3,12 @@ * @module tutils/types/tests/unit-d/IsObject */ -import type Fn from '../fn' import type TestSubject from '../is-object' -import type ObjectCurly from '../object-curly' -import type Primitive from '../primitive' +import type Nilable from '../nilable' describe('unit-d:types/IsObject', () => { - it('should equal false if [T] does not extend [object]', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend object', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -22,11 +19,13 @@ describe('unit-d:types/IsObject', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [object]', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal true if T extends object', () => { expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-primitive.spec-d.ts b/src/types/__tests__/is-primitive.spec-d.ts index c408e7f2..29441059 100644 --- a/src/types/__tests__/is-primitive.spec-d.ts +++ b/src/types/__tests__/is-primitive.spec-d.ts @@ -4,13 +4,13 @@ */ import type TestSubject from '../is-primitive' +import type JsonPrimitive from '../json-primitive' import type OneOrMany from '../one-or-many' import type Primitive from '../primitive' describe('unit-d:types/IsPrimitive', () => { - it('should equal false if [T] does not extend [Primitive]', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>>().toEqualTypeOf() + it('should equal false if T does not extend Primitive', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -21,7 +21,21 @@ describe('unit-d:types/IsPrimitive', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [Primitive]', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal true if T extends Primitive', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-string.spec-d.ts b/src/types/__tests__/is-string.spec-d.ts index 06a339f3..cc6e631e 100644 --- a/src/types/__tests__/is-string.spec-d.ts +++ b/src/types/__tests__/is-string.spec-d.ts @@ -8,9 +8,8 @@ import type Nilable from '../nilable' import type Timestamp from '../timestamp' describe('unit-d:types/IsString', () => { - it('should equal false if [T] does not extend [string]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend string', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -21,8 +20,14 @@ describe('unit-d:types/IsString', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [string]', () => { + it('should equal true if T extends string', () => { expectTypeOf>>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) + }) }) diff --git a/src/types/__tests__/is-symbol.spec-d.ts b/src/types/__tests__/is-symbol.spec-d.ts index b4bffcfa..6cba0550 100644 --- a/src/types/__tests__/is-symbol.spec-d.ts +++ b/src/types/__tests__/is-symbol.spec-d.ts @@ -4,12 +4,11 @@ */ import type TestSubject from '../is-symbol' -import type Optional from '../optional' +import type Nilable from '../nilable' describe('unit-d:types/IsSymbol', () => { - it('should equal false if [T] does not extend [symbol]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend symbol', () => { + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -20,7 +19,13 @@ describe('unit-d:types/IsSymbol', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [symbol]', () => { + it('should equal true if T extends symbol', () => { expectTypeOf>().toEqualTypeOf() }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) + }) }) diff --git a/src/types/__tests__/is-true.spec-d.ts b/src/types/__tests__/is-true.spec-d.ts new file mode 100644 index 00000000..ee83c519 --- /dev/null +++ b/src/types/__tests__/is-true.spec-d.ts @@ -0,0 +1,33 @@ +/** + * @file Type Tests - IsTrue + * @module tutils/types/tests/unit-d/IsTrue + */ + +import type Booleanish from '../booleanish' +import type TestSubject from '../is-true' + +describe('unit-d:types/IsTrue', () => { + it('should equal false if T does not extend true', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal false if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal true if T extends true', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/is-tuple.spec-d.ts b/src/types/__tests__/is-tuple.spec-d.ts index c79309c8..2566e81f 100644 --- a/src/types/__tests__/is-tuple.spec-d.ts +++ b/src/types/__tests__/is-tuple.spec-d.ts @@ -3,13 +3,15 @@ * @module tutils/types/tests/unit-d/IsTuple */ +import type Vehicle from '#fixtures/vehicle' +import type EmptyArray from '../empty-array' import type TestSubject from '../is-tuple' import type Nilable from '../nilable' describe('unit-d:types/IsTuple', () => { - it('should equal false if [T] does extend [[infer U, ...infer Rest]]', () => { - expectTypeOf>>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + it('should equal false if T does not extend a tuple', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -20,7 +22,19 @@ describe('unit-d:types/IsTuple', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [[infer U, ...infer Rest]]', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal true if T extends a tuple', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable<['vin', Vehicle['vin']]> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/is-undefined.spec-d.ts b/src/types/__tests__/is-undefined.spec-d.ts index 1aa79778..81a82c4d 100644 --- a/src/types/__tests__/is-undefined.spec-d.ts +++ b/src/types/__tests__/is-undefined.spec-d.ts @@ -4,12 +4,12 @@ */ import type TestSubject from '../is-undefined' -import type Optional from '../optional' +import type Nilable from '../nilable' describe('unit-d:types/IsUndefined', () => { - it('should equal false if [T] does not extend [undefined]', () => { - expectTypeOf>>().toEqualTypeOf() + it('should equal false if T does not extend undefined', () => { expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() }) it('should equal false if T is any', () => { @@ -20,7 +20,13 @@ describe('unit-d:types/IsUndefined', () => { expectTypeOf>().toEqualTypeOf() }) - it('should equal true if [T] extends [undefined]', () => { + it('should equal true if T extends undefined', () => { expectTypeOf>().toEqualTypeOf() }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>>().toEqualTypeOf() + }) + }) }) diff --git a/src/types/__tests__/join.spec-d.ts b/src/types/__tests__/join.spec-d.ts index 9f1d05bc..372b88ba 100644 --- a/src/types/__tests__/join.spec-d.ts +++ b/src/types/__tests__/join.spec-d.ts @@ -3,31 +3,71 @@ * @module tutils/types/tests/unit-d/Join */ -import type Author from '#fixtures/author.interface' +import type Vehicle from '#fixtures/vehicle' +import type Dot from '../dot' import type EmptyArray from '../empty-array' +import type EmptyString from '../empty-string' +import type Indices from '../indices' import type TestSubject from '../join' +import type NIL from '../nil' +import type Nullable from '../nullable' +import type Optional from '../optional' describe('unit-d:types/Join', () => { - it('should equal A.join(Delimiter)', () => { - // Arrange - type A1 = EmptyArray - type A2 = [1, 2, 3] - type A3 = [1n, 2n, 3n] - type A4 = [false, true, false] - type A5 = ['arr', ...number[]] - type A6 = [...A5, keyof Author] - - // Expect - expectTypeOf>().toEqualTypeOf<''>() - expectTypeOf>().toEqualTypeOf<'1.2.3'>() - expectTypeOf>().toEqualTypeOf<'1.2.3'>() - expectTypeOf>().toEqualTypeOf<'false.true.false'>() - expectTypeOf>().toEqualTypeOf<`arr.${string}`>() - expectTypeOf>().toEqualTypeOf< - | `arr.${string}.display_name` - | `arr.${string}.email` - | `arr.${string}.first_name` - | `arr.${string}.last_name` - >() + it('should equal string if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends readonly Joinable[]', () => { + type Vehicles = [Vehicle, Vehicle, Vehicle] + + it('should equal T.join(S)', () => { + // Arrange + type T1 = Readonly + type T2 = [1, 2, 3] + type T3 = [1n, 2n, 3n] + type T4 = [false, true, false, true] + type T5 = [Optional<'hello'>, NIL, Nullable<'world'>] + type T6 = ['foo', undefined, 'bar', null, 'baz'] + type T7 = ['prefix', ...string[]] + type T8 = ['data', Indices] + type T9 = [...T8, keyof Vehicle] + type E1 = EmptyString + type E2 = `${T2[0]}${Dot}${T2[1]}${Dot}${T2[2]}` + type E3 = E2 + type E4 = `${T4[0]}${Dot}${T4[1]}${Dot}${T4[2]}${Dot}${T4[3]}` + type E5 = '..' | '..world' | 'hello..' | 'hello..world' + type E6 = `${T6[0]}${Dot}${Dot}${T6[2]}${Dot}${Dot}${T6[4]}` + type E7 = `${T7[0]}${Dot}${string}` + type E8 = `${T8[0]}${Dot}${Indices}` + type E9 = `${E8}${Dot}${keyof Vehicle}` + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = ['foo', 'bar'] | ['hello', 'world'] + type S = Dot | '_' + type Expect = `foo${S}bar` | `hello${S}world` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/json-array.spec-d.ts b/src/types/__tests__/json-array.spec-d.ts index 4207111f..98567e92 100644 --- a/src/types/__tests__/json-array.spec-d.ts +++ b/src/types/__tests__/json-array.spec-d.ts @@ -8,12 +8,10 @@ import type JsonValue from '../json-value' describe('unit-d:types/JsonArray', () => { it('should extract JsonValue[]', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) - it('should match readonly JsonValue[]', () => { - expectTypeOf().toMatchTypeOf() + it('should extract readonly JsonValue[]', () => { + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/json-object.spec-d.ts b/src/types/__tests__/json-object.spec-d.ts index 940620ec..1c0050df 100644 --- a/src/types/__tests__/json-object.spec-d.ts +++ b/src/types/__tests__/json-object.spec-d.ts @@ -7,11 +7,15 @@ import type TestSubject from '../json-object' import type JsonValue from '../json-value' describe('unit-d:types/JsonObject', () => { - it('should have keys of type string', () => { - expectTypeOf().toBeString() + describe('keys', () => { + it('should equal string', () => { + expectTypeOf().toBeString() + }) }) - it('should have properties of type JsonValue', () => { - expectTypeOf().toEqualTypeOf() + describe('properties', () => { + it('should equal JsonValue', () => { + expectTypeOf().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/json-primitive.spec-d.ts b/src/types/__tests__/json-primitive.spec-d.ts index eb09f967..73ff96d1 100644 --- a/src/types/__tests__/json-primitive.spec-d.ts +++ b/src/types/__tests__/json-primitive.spec-d.ts @@ -8,9 +8,7 @@ import type NumberString from '../number-string' describe('unit-d:types/JsonPrimitive', () => { it('should extract NumberString', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract boolean', () => { diff --git a/src/types/__tests__/json-value.spec-d.ts b/src/types/__tests__/json-value.spec-d.ts index 8310fcf6..f97bbb29 100644 --- a/src/types/__tests__/json-value.spec-d.ts +++ b/src/types/__tests__/json-value.spec-d.ts @@ -10,18 +10,14 @@ import type TestSubject from '../json-value' describe('unit-d:types/JsonValue', () => { it('should extract JsonArray', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract JsonObject', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract JsonPrimitive', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/jsonifiable-instance.spec-d.ts b/src/types/__tests__/jsonifiable-instance.spec-d.ts index a3f9d43b..923ca94c 100644 --- a/src/types/__tests__/jsonifiable-instance.spec-d.ts +++ b/src/types/__tests__/jsonifiable-instance.spec-d.ts @@ -7,7 +7,7 @@ import type Jsonifiable from '../jsonifiable' import type TestSubject from '../jsonifiable-instance' describe('unit-d:types/JsonifiableInstance', () => { - it('should match [toJSON(): Jsonifiable]', () => { + it('should match [toJSON: () => Jsonifiable]', () => { expectTypeOf() .toHaveProperty('toJSON') .toEqualTypeOf<() => Jsonifiable>() diff --git a/src/types/__tests__/jsonifiable-object.spec-d.ts b/src/types/__tests__/jsonifiable-object.spec-d.ts index 47a2b419..8a435b3e 100644 --- a/src/types/__tests__/jsonifiable-object.spec-d.ts +++ b/src/types/__tests__/jsonifiable-object.spec-d.ts @@ -8,11 +8,17 @@ import type TestSubject from '../jsonifiable-object' import type Optional from '../optional' describe('unit-d:types/JsonifiableObject', () => { - it('should have keys of type string', () => { - expectTypeOf().toBeString() + describe('keys', () => { + it('should equal string', () => { + expectTypeOf().toBeString() + }) }) - it('should have properties of type Optional', () => { - expectTypeOf().toEqualTypeOf>() + describe('properties', () => { + it('should equal JsonValue', () => { + expectTypeOf().toEqualTypeOf< + Optional + >() + }) }) }) diff --git a/src/types/__tests__/jsonifiable.spec-d.ts b/src/types/__tests__/jsonifiable.spec-d.ts index dd9672a1..6f10b84c 100644 --- a/src/types/__tests__/jsonifiable.spec-d.ts +++ b/src/types/__tests__/jsonifiable.spec-d.ts @@ -11,26 +11,18 @@ import type JsonifiableObject from '../jsonifiable-object' describe('unit-d:types/Jsonifiable', () => { it('should extract JsonifiableArray', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract JsonifiableInstance', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract JsonifiableObject', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract JsonPrimitive', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/keyof.spec-d.ts b/src/types/__tests__/keyof.spec-d.ts new file mode 100644 index 00000000..da52abb5 --- /dev/null +++ b/src/types/__tests__/keyof.spec-d.ts @@ -0,0 +1,222 @@ +/** + * @file Type Tests - Keyof + * @module tutils/types/tests/unit-d/Keyof + */ + +import type Vehicle from '#fixtures/vehicle' +import type EmptyObject from '../empty-object' +import type Fn from '../fn' +import type Indices from '../indices' +import type TestSubject from '../keyof' +import type NIL from '../nil' +import type Nilable from '../nilable' +import type NumberLike from '../number-like' +import type { tag } from '../opaque' + +describe('unit-d:types/Keyof', () => { + it('should equal keyof T if T is any', () => { + // Arrange + type T = any + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is unknown', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends ObjectCurly', () => { + it('should equal keyof T', () => { + // Arrange + type T1 = { [x: number]: never; [x: string]: never; [x: symbol]: never } + type T2 = Vehicle + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if typeof EmptyObjectTag extends keyof T', () => { + expectTypeOf>().toBeNever() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = Vehicle & { readonly [tag]: 'vehicle' } + type F = symbol + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends Primitive', () => { + it('should equal never if keyof T is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + + describe('T extends bigint', () => { + it('should equal keyof T', () => { + // Arrange + type T = bigint + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = bigint & { readonly [tag]: 'bigint' } + type F = symbol + type Expect = typeof Symbol.toStringTag | typeof tag + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends boolean', () => { + it('should equal keyof T', () => { + // Arrange + type T = boolean + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = boolean & { readonly [tag]: 'boolean' } + type F = symbol + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends number', () => { + it('should equal keyof T', () => { + // Arrange + type T = number + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = number & { readonly [tag]: 'number' } + type F = symbol + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends string', () => { + it('should equal Exclude | Indices', () => { + // Arrange + type T1 = 'xyz' + type T2 = string + type Expect = + | Exclude + | Indices + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = 'abc' + + // Expect + expectTypeOf>().toEqualTypeOf>() + }) + }) + + describe('T extends symbol', () => { + it('should equal keyof T', () => { + // Arrange + type T = symbol + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = symbol + type F = symbol + type Expect = typeof Symbol.toPrimitive | typeof Symbol.toStringTag + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Readonly', () => { + it('should equal keyof T', () => { + // Arrange + type T1 = Fn + type T2 = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = Readonly + type F = symbol + type Expect = typeof Symbol.hasInstance + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends readonly unknown[]', () => { + it('should equal Exclude | Indices', () => { + // Arrange + type T1 = ['x', 'y', 'z'?] + type T2 = string[] + type Expect = + | Exclude + | Indices + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + + it('should extract F from keyof T', () => { + // Arrange + type T = ['a', 'b', 'c'?] + + // Expect + expectTypeOf>().toEqualTypeOf>() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable + type Expect = keyof Vehicle | keyof Vehicle['vin'] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/keys-optional-exact.spec-d.ts b/src/types/__tests__/keys-optional-exact.spec-d.ts new file mode 100644 index 00000000..58fe5480 --- /dev/null +++ b/src/types/__tests__/keys-optional-exact.spec-d.ts @@ -0,0 +1,150 @@ +/** + * @file Type Tests - ExactOptionalKeys + * @module tutils/types/tests/unit-d/ExactOptionalKeys + */ + +import type Author from '#fixtures/author.interface' +import type Vehicle from '#fixtures/vehicle' +import type EmptyObject from '../empty-object' +import type Fn from '../fn' +import type Indices from '../indices' +import type TestSubject from '../keys-optional-exact' +import type NIL from '../nil' +import type Optional from '../optional' + +describe('unit-d:types/ExactOptionalKeys', () => { + it('should equal never if T is any', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is unknown', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends ObjectCurly', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = Author + type T2 = Partial + + // Expect + expectTypeOf>().toEqualTypeOf<'email'>() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + }) + }) + + describe('T extends Primitive', () => { + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + + describe('T extends bigint', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = bigint + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a'>() + }) + }) + + describe('T extends boolean', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = boolean + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a'>() + }) + }) + + describe('T extends number', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = number + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a'>() + }) + }) + + describe('T extends string', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = string + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a'>() + }) + }) + + describe('T extends symbol', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = symbol + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a'>() + }) + }) + }) + + describe('T extends Readonly', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = Fn + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a'>() + }) + }) + + describe('T extends readonly unknown[]', () => { + it('should construct union of exact optional keys', () => { + // Arrange + type T1 = string[] | ['a'] | [Optional<'b'>] + type T2 = ['a'?] + type T3 = T1 & { readonly a?: string; b?: string | undefined } + type T4 = Pick & T2 + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf<'a'>() + expectTypeOf>().toEqualTypeOf | 'a'>() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Author | [Author?] + type Expect = Indices<[Author?]> | 'email' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/keys-optional.spec-d.ts b/src/types/__tests__/keys-optional.spec-d.ts index 96081ba4..e5c46896 100644 --- a/src/types/__tests__/keys-optional.spec-d.ts +++ b/src/types/__tests__/keys-optional.spec-d.ts @@ -3,14 +3,167 @@ * @module tutils/types/tests/unit-d/OptionalKeys */ +import type Author from '#fixtures/author.interface' +import type Vehicle from '#fixtures/vehicle' +import type Fn from '../fn' +import type Indices from '../indices' import type TestSubject from '../keys-optional' +import type NIL from '../nil' +import type Optional from '../optional' describe('unit-d:types/OptionalKeys', () => { - it('should extract optional keys from T', () => { - // Arrange - type T = { display_name?: string; username: string } + it('should equal never if T is any', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is unknown', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends ObjectCurly', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = Author + type T2 = Partial + + // Expect + expectTypeOf>().toEqualTypeOf<'display_name' | 'email'>() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if keyof T is never', () => { + expectTypeOf>().toBeNever() + }) + }) + + describe('T extends Primitive', () => { + it('should equal never if keyof T is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + + describe('T extends bigint', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = bigint + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a' | 'b'>() + }) + }) + + describe('T extends boolean', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = boolean + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a' | 'b'>() + }) + }) + + describe('T extends number', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = number + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a' | 'b'>() + }) + }) + + describe('T extends string', () => { + describe('IsLiteral extends true', () => { + it('should construct union of optional keys', () => { + expectTypeOf>().toBeNever() + }) + }) + + describe('number extends Length', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = string + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a' | 'b'>() + }) + }) + }) + + describe('T extends symbol', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = symbol + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a' | 'b'>() + }) + }) + }) + + describe('T extends Readonly', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = Fn + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a' | 'b'>() + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = readonly [Vehicle, Optional] + type T2 = readonly [...T1, Vehicle?] + type T3 = T2 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<-1 | 2>() + expectTypeOf>().toEqualTypeOf<-1 | 'a' | 'b' | 2>() + }) + }) + + describe('number extends Length', () => { + it('should construct union of optional keys', () => { + // Arrange + type T1 = Vehicle[] + type T2 = T1 & { readonly a?: string; b?: string | undefined } + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf<'a' | 'b'>() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Author | [Author?] + type Expect = Indices<[Author?]> | 'display_name' | 'email' - // Expect - expectTypeOf>().toEqualTypeOf<'display_name'>() + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/keys-readonly.spec-d.ts b/src/types/__tests__/keys-readonly.spec-d.ts new file mode 100644 index 00000000..34dbcb82 --- /dev/null +++ b/src/types/__tests__/keys-readonly.spec-d.ts @@ -0,0 +1,150 @@ +/** + * @file Type Tests - ReadonlyKeys + * @module tutils/types/tests/unit-d/ReadonlyKeys + */ + +import type Vehicle from '#fixtures/vehicle' +import type EmptyObject from '../empty-object' +import type Fn from '../fn' +import type Indices from '../indices' +import type TestSubject from '../keys-readonly' +import type NIL from '../nil' +import type { tag } from '../opaque' + +describe('unit-d:types/ReadonlyKeys', () => { + it('should equal never if T is any', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is unknown', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends ObjectCurly', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T1 = Vehicle + type T2 = T1 & { readonly [tag]: 'vehicle'; readonly id?: string } + type T3 = Readonly + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + }) + }) + + describe('T extends Primitive', () => { + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + + describe('T extends bigint', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T = bigint & { readonly [tag]: 'bigint'; readonly id?: string } + type Expect = typeof Symbol.toStringTag | typeof tag | 'id' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends boolean', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T = boolean & { readonly [tag]: 'boolean'; readonly id?: string } + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends number', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T = number & { readonly [tag]: 'number'; readonly id?: string } + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends string', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T = string & { readonly [tag]: 'string'; readonly id?: string } + type Expect = typeof tag | 'id' | 'length' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends symbol', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T = symbol + type Expect = typeof Symbol.toStringTag | 'description' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Readonly', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T1 = Fn + type T2 = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf<'length' | 'name'>() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + it('should construct union of readonly keys', () => { + // Arrange + type T = readonly [Vehicle['vin'], Vehicle] + type Expect = Indices | typeof Symbol.unscopables | 'length' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends Length', () => { + it('should construct union of readonly keys', () => { + // Arrange + type Expect = typeof Symbol.unscopables | 'length' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Readonly | { readonly [tag]: 'vehicle' } + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/keys-required.spec-d.ts b/src/types/__tests__/keys-required.spec-d.ts index 31371d50..e72f5afc 100644 --- a/src/types/__tests__/keys-required.spec-d.ts +++ b/src/types/__tests__/keys-required.spec-d.ts @@ -3,14 +3,184 @@ * @module tutils/types/tests/unit-d/RequiredKeys */ +import type Vehicle from '#fixtures/vehicle' +import type EmptyObject from '../empty-object' +import type Fn from '../fn' +import type Indices from '../indices' import type TestSubject from '../keys-required' +import type NIL from '../nil' +import type NumberLike from '../number-like' +import type { tag } from '../opaque' +import type Optional from '../optional' +import type Timestamp from '../timestamp' describe('unit-d:types/RequiredKeys', () => { - it('should extract required keys from T', () => { - // Arrange - type T = { display_name?: string; username: string } + it('should equal never if T is any', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is unknown', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends ObjectCurly', () => { + it('should construct union of required keys', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + }) + }) + + describe('T extends Primitive', () => { + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + + describe('T extends bigint', () => { + it('should construct union of required keys', () => { + // Arrange + type T1 = bigint + type T2 = T1 & { readonly a: 1; b?: 1; c?: 1 | undefined } + type E1 = keyof T1 + type E2 = keyof Omit + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends boolean', () => { + it('should construct union of required keys', () => { + // Arrange + type T1 = boolean + type T2 = T1 & { readonly a: 1; b?: 1; c?: 1 | undefined } + type E1 = keyof T1 + type E2 = keyof Omit + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends number', () => { + it('should construct union of required keys', () => { + // Arrange + type T1 = number + type T2 = T1 & { readonly a: 1; b?: 1; c?: 1 | undefined } + type T3 = Timestamp<'unix'> + type E1 = keyof number + type E2 = keyof Omit + type E3 = E1 | typeof tag + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends string', () => { + describe('IsLiteral extends true', () => { + it('should construct union of required keys', () => { + // Arrange + type T = 'abc' + type Expect = Exclude | Indices + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends Indices', () => { + it('should construct union of required keys', () => { + // Arrange + type T1 = string + type T2 = string & { readonly a: 1; b?: 1; c?: 1 | undefined } + type E1 = keyof T1 + type E2 = Exclude + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends symbol', () => { + it('should construct union of required keys', () => { + // Arrange + type T1 = symbol + type T2 = T1 & { readonly a: 1; b?: 1; c?: 1 | undefined } + type E1 = keyof T1 + type E2 = keyof Omit + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends Readonly', () => { + it('should construct union of required keys', () => { + // Arrange + type T1 = Fn + type T2 = T1 & { readonly a: 1; b?: 1; c?: 1 | undefined } + type E1 = keyof T1 + type E2 = keyof Omit + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + it('should construct union of required keys', () => { + // Arrange + type T = readonly [Vehicle, Optional, Vehicle?] + type Expect = Exclude | -2 | -3 | 0 | 1 + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends Indices', () => { + it('should construct union of required keys', () => { + // Arrange + type T1 = Vehicle[] & { readonly a: 1; b?: 1; c?: 1 | undefined } + type T2 = Partial + type E1 = Exclude + type E2 = keyof T2 + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Vehicle | [Vehicle] - // Expect - expectTypeOf>().toEqualTypeOf<'username'>() + // Expect + expectTypeOf>().toEqualTypeOf< + Exclude | Indices<[Vehicle]> | keyof Vehicle + >() + }) }) }) diff --git a/src/types/__tests__/keys.spec-d.ts b/src/types/__tests__/keys.spec-d.ts deleted file mode 100644 index 432c285d..00000000 --- a/src/types/__tests__/keys.spec-d.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file Type Tests - Keys - * @module tutils/types/tests/unit-d/Keys - */ - -import type Author from '#fixtures/author.interface' -import type EmptyArray from '../empty-array' -import type EmptyObject from '../empty-object' -import type EmptyString from '../empty-string' -import type Fn from '../fn' -import type Indices from '../indices' -import type TestSubject from '../keys' -import type Numeric from '../numeric' -import type Simplify from '../simplify' -import type Timestamp from '../timestamp' - -describe('unit-d:types/Keys', () => { - it('should equal never if T extends EmptyArray', () => { - expectTypeOf>().toBeNever() - }) - - it('should equal never if T extends EmptyObject', () => { - expectTypeOf>().toBeNever() - }) - - it('should equal never if T extends EmptyString', () => { - expectTypeOf>().toBeNever() - }) - - it('should equal union if T extends ObjectCurly', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>>().toEqualTypeOf() - }) - - it('should equal union if T extends Readonly>', () => { - // Arrange - type B1 = Date - type B2 = Map - class B3 extends Set {} - - // Expect - expectTypeOf>().toBeNever() - expectTypeOf>().toBeNever() - expectTypeOf>().toBeNever() - }) - - it('should equal union if T extends Readonly', () => { - // Arrange - type F1 = Fn - type F2 = Fn & { id: string } - type F3 = Readonly - - // Expect - expectTypeOf>().toBeNever() - expectTypeOf>().toEqualTypeOf<'id'>() - expectTypeOf>().toEqualTypeOf<'id'>() - }) - - it('should equal union if T extends Readonly', () => { - // Arrange - type P1 = boolean - type P2 = string & { tag: string } - type P3 = Readonly - type P4 = Timestamp<'unix'> - - // Expect - expectTypeOf>().toBeNever() - expectTypeOf>().toEqualTypeOf<'tag'>() - expectTypeOf>().toEqualTypeOf<'tag'>() - expectTypeOf>().toBeNever() - }) - - it('should equal union if T extends readonly unknown[]', () => { - // Arrange - type A1 = Author[] | null - type A2 = Author[] & { count: number } - type A3 = [Author, Author] | null - type A4 = EmptyArray - type A5 = EmptyArray & { count: 0 } - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf>() - expectTypeOf>().toEqualTypeOf>() - expectTypeOf>().toEqualTypeOf | 'count'>() - }) -}) diff --git a/src/types/__tests__/length.spec-d.ts b/src/types/__tests__/length.spec-d.ts new file mode 100644 index 00000000..5f8ec99f --- /dev/null +++ b/src/types/__tests__/length.spec-d.ts @@ -0,0 +1,45 @@ +/** + * @file Type Tests - Length + * @module tutils/types/tests/unit-d/Length + */ + +import type Vehicle from '#fixtures/vehicle' +import type EmptyArray from '../empty-array' +import type EmptyString from '../empty-string' +import type TestSubject from '../length' +import type Split from '../split' + +describe('unit-d:types/Length', () => { + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal number if T is any', () => { + expectTypeOf>().toBeNumber() + }) + + describe('T extends { readonly length: number }', () => { + it('should equal T["length"]', () => { + expectTypeOf>().toEqualTypeOf<0 | 1>() + expectTypeOf>>().toEqualTypeOf<0>() + expectTypeOf>().toBeNumber() + expectTypeOf>().toEqualTypeOf<0 | 1>() + }) + + describe('T extends string', () => { + type Expected = Split['length'] + + it('should equal Split["length"]', () => { + // Arrange + type T1 = string + type T2 = 'abcdefghijklmnopqrstuvwyz' + type T3 = EmptyString + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + }) +}) diff --git a/src/types/__tests__/literal-union.spec-d.ts b/src/types/__tests__/literal-union.spec-d.ts index 6eeb6c91..c880bfc8 100644 --- a/src/types/__tests__/literal-union.spec-d.ts +++ b/src/types/__tests__/literal-union.spec-d.ts @@ -10,12 +10,12 @@ describe('unit-d:types/LiteralUnion', () => { type P = string it('should extract L', () => { - expectTypeOf>().extract().toEqualTypeOf() + expectTypeOf>().extract().not.toBeNever() }) it('should extract P & Record', () => { expectTypeOf>() .extract

>() - .toEqualTypeOf

>() + .not.toBeNever() }) }) diff --git a/src/types/__tests__/merge-defaults.spec-d.ts b/src/types/__tests__/merge-defaults.spec-d.ts deleted file mode 100644 index e92dc4c0..00000000 --- a/src/types/__tests__/merge-defaults.spec-d.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file Type Tests - MergeDefaults - * @module tutils/types/tests/unit-d/MergeDefaults - */ - -import type Author from '#fixtures/author.interface' -import type EmptyArray from '../empty-array' -import type Merge from '../merge' -import type TestSubject from '../merge-defaults' - -describe('unit-d:types/MergeDefaults', () => { - it('should equal T if U extends EmptyArray', () => { - expectTypeOf>().toEqualTypeOf() - }) - - it('should equal T if U extends EmptyObject', () => { - expectTypeOf>().toEqualTypeOf() - }) - - it('should merge defaults into T if U extends ObjectCurly', () => { - // Arrange - type U = { email: Lowercase; first_name?: string } - type Expected = Merge> - - // Expect - expectTypeOf>().toEqualTypeOf() - }) - - it('should merge defaults into T if U extends readonly ObjectCurly[]', () => { - // Arrange - type U1 = { display_name: string; first_name?: string }[] - type U2 = [{ display_name: string }, { first_name?: string }] - type E1 = Merge> - type E2 = Merge - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - }) -}) diff --git a/src/types/__tests__/merge.spec-d.ts b/src/types/__tests__/merge.spec-d.ts index 9e22cef6..ef9bf896 100644 --- a/src/types/__tests__/merge.spec-d.ts +++ b/src/types/__tests__/merge.spec-d.ts @@ -4,21 +4,304 @@ */ import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' +import type Assign from '../assign' +import type EmptyArray from '../empty-array' +import type EmptyObject from '../empty-object' import type TestSubject from '../merge' -import type Simplify from '../simplify' +import type Nilable from '../nilable' +import type Nullable from '../nullable' +import type { tag } from '../opaque' +import type PropertyKey from '../property-key' describe('unit-d:types/Merge', () => { - it('should equal Simplify & U>', () => { + type T = Book + + it('should equal Record & T if U is any', () => { // Arrange - type T = Author - type U = { display_name: string } - type Expected = Simplify & U> + type Expect = Record & T // Expect - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('U extends ObjectCurly', () => { + it('should equal T if HasKeys extends false', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + it('should merge T and U', () => { + // Arrange + type U = { + readonly [tag]: 'book' + authors: string[] + readonly id?: string + readonly publisher: string + } + + // Expect + expectTypeOf>().toEqualTypeOf>() + }) + + it('should recursively merge nested pojos', () => { + // Arrange + type U = { + readonly publisher?: Nullable<{ + display_name: { readonly [tag]: 'display_name' } + phone: number + }> + } + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & { + readonly publisher?: Nullable< + Assign< + T['publisher'], + { + display_name: { readonly [tag]: 'display_name'; value: string } + phone: number + } + > + > + } + >() + }) + + it('should skip mutual properties in U that are undefined', () => { + // Arrange + type U = { readonly id: undefined; readonly isbn: undefined } + + // Expect + expectTypeOf>().toEqualTypeOf & T>() + }) + + describe('M["concat"] extends true', () => { + it('should concat arrays', () => { + // Arrange + type U = { authors: string[] } + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & { authors: (Author | string)[] } + >() + }) + }) + + describe('M["defaults"] extends true', () => { + it('should merge U with optional properties of T', () => { + // Arrange + type T = Omit & { readers: Set | undefined } + type U = { + readonly [tag]: 'book' + isbn?: number + publisher: NonNullable + readers: Set + } + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & Omit + >() + }) + }) }) - it('should equal T if U extends EmptyObject', () => { - expectTypeOf>().toEqualTypeOf() + describe('U extends readonly ObjectCurly[]', () => { + it('should equal T if U extends Readonly', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + }) + + it('should equal T if U extends readonly EmptyObject[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('U extends readonly [(infer H)?, ...infer R]', () => { + it('should merge T and H', () => { + // Arrange + type U1 = (Readonly & { readonly [tag]: 'book' })[] + type U2 = (Readonly | { readonly [tag]: 'book' })[] + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + + it('should recursively merge nested pojos', () => { + // Arrange + type U = { + readonly publisher?: Nullable<{ + display_name: { readonly [tag]: 'display_name' } + phone: number + }> + }[] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & { + readonly publisher?: Nullable< + Assign< + T['publisher'], + { + display_name: { + readonly [tag]: 'display_name' + value: string + } + phone: number + } + > + > + } + >() + }) + + it('should skip mutual properties in H that are undefined', () => { + // Arrange + type U = { readonly id: undefined; readonly isbn: undefined }[] + type Expect = Omit & T + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('M["concat"] extends true', () => { + it('should concat arrays', () => { + // Arrange + type U = { authors: string[] }[] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & { authors: (Author | string)[] } + >() + }) + }) + + describe('M["defaults"] extends true', () => { + it('should merge H with optional properties of T', () => { + // Arrange + type T = Omit & { readers: Set | undefined } + type U = { + readonly [tag]: 'book' + isbn?: number + publisher: NonNullable + readers: Set + }[] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & Omit + >() + }) + }) + }) + + describe('U extends readonly [infer H, ...infer R]', () => { + it('should merge T and U[i]', () => { + // Arrange + type U = [EmptyObject, { owner?: string }, T, { readonly isbn: number }] + + // Expect + expectTypeOf>().toEqualTypeOf>() + }) + + it('should recursively merge nested pojos', () => { + // Arrange + type U = [ + { publisher: { display_name: { readonly [tag]: 'display_name' } } }, + { publisher: { phone: number } }, + any, + never, + { readonly publisher: { location: string } }, + { readonly publisher: Nullable<{}> } + ] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & { + [x: number]: any + [x: string]: any + [x: symbol]: any + readonly publisher: Nullable< + Assign< + T['publisher'], + { + display_name: { + readonly [tag]: 'display_name' + value: string + } + location: string + phone: number + } + > + > + } + >() + }) + + it('should skip mutual properties in U[i] that are undefined', () => { + // Arrange + type U = [{ readonly id: undefined }, { readonly isbn: undefined }] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('M["concat"] extends true', () => { + it('should concat arrays', () => { + // Arrange + type U = [{ authors: string[] }] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & { authors: (Author | string)[] } + >() + }) + }) + + describe('M["defaults"] extends true', () => { + it('should merge U[i] with optional properties of T', () => { + // Arrange + type T = Omit & { readers: Set | undefined } + type U = [ + { readonly [tag]: 'book' }, + { isbn?: number }, + { publisher: NonNullable }, + { readers: Set } + ] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & U[0] & U[2] + >() + }) + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable + type U = + | ({ readonly [tag]: string } | { readonly id?: string })[] + | EmptyObject + | [{ readonly [tag]: string }, { readonly id?: string }] + + // Expect + expectTypeOf>().toEqualTypeOf< + | NonNullable + | (T & ({ readonly [tag]: string } & { readonly id?: string })) + | (T & ({ readonly [tag]: string } | { readonly id?: string })) + >() + }) }) }) diff --git a/src/types/__tests__/number-like.spec-d.ts b/src/types/__tests__/number-like.spec-d.ts index 693c0f89..03cde9ba 100644 --- a/src/types/__tests__/number-like.spec-d.ts +++ b/src/types/__tests__/number-like.spec-d.ts @@ -8,10 +8,10 @@ import type Numeric from '../numeric' describe('unit-d:types/NumberLike', () => { it('should extract Numeric', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract number', () => { - expectTypeOf().extract().toBeNumber() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/number-string.spec-d.ts b/src/types/__tests__/number-string.spec-d.ts index b1c94783..2cdedbde 100644 --- a/src/types/__tests__/number-string.spec-d.ts +++ b/src/types/__tests__/number-string.spec-d.ts @@ -7,10 +7,10 @@ import type TestSubject from '../number-string' describe('unit-d:types/NumberString', () => { it('should extract number', () => { - expectTypeOf().extract().toBeNumber() + expectTypeOf().extract().not.toBeNever() }) it('should extract string', () => { - expectTypeOf().extract().toBeString() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/numeric-negative.spec-d.ts b/src/types/__tests__/numeric-negative.spec-d.ts new file mode 100644 index 00000000..803572e3 --- /dev/null +++ b/src/types/__tests__/numeric-negative.spec-d.ts @@ -0,0 +1,13 @@ +/** + * @file Type Tests - NegativeNumeric + * @module tutils/types/tests/unit-d/NegativeNumeric + */ + +import type Numeric from '../numeric' +import type TestSubject from '../numeric-negative' + +describe('unit-d:types/NegativeNumeric', () => { + it('should equal `-${Numeric}`', () => { + expectTypeOf().toEqualTypeOf<`-${Numeric}`>() + }) +}) diff --git a/src/types/__tests__/numeric.spec-d.ts b/src/types/__tests__/numeric.spec-d.ts index 1a6204d8..0a23cf55 100644 --- a/src/types/__tests__/numeric.spec-d.ts +++ b/src/types/__tests__/numeric.spec-d.ts @@ -7,15 +7,7 @@ import type TestSubject from '../numeric' import type Stringify from '../stringify' describe('unit-d:types/Numeric', () => { - it('should equal Stringify`', () => { + it('should equal Stringify', () => { expectTypeOf().toEqualTypeOf>() }) - - it('should match positive numeric', () => { - expectTypeOf<'13'>().toMatchTypeOf() - }) - - it('should match negative numeric', () => { - expectTypeOf<'-13'>().toMatchTypeOf() - }) }) diff --git a/src/types/__tests__/omit-index-signature.spec-d.ts b/src/types/__tests__/omit-index-signature.spec-d.ts new file mode 100644 index 00000000..e30f9576 --- /dev/null +++ b/src/types/__tests__/omit-index-signature.spec-d.ts @@ -0,0 +1,40 @@ +/** + * @file Type Tests - OmitIndexSignature + * @module tutils/types/tests/unit-d/OmitIndexSignature + */ + +import type Dot from '../dot' +import type TestSubject from '../omit-index-signature' + +describe('unit-d:types/OmitIndexSignature', () => { + it('should remove index signatures from T', () => { + // Arrange + type T = { + [x: number]: any + [x: string]: any + [x: symbol]: any + [x: `${bigint}`]: string + [x: `${number}`]: string + [x: `data${Dot}${string}`]: string + hello: 'world' + foo?: 'bar' + } + + // Expect + expectTypeOf>().toEqualTypeOf<{ + hello: 'world' + foo?: 'bar' + }>() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = { [x: number]: number; a: 0 } | { [x: string]: number; z?: 25 } + type Expect = { a: 0 } | { z?: 25 } + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/omit-native.spec-d.ts b/src/types/__tests__/omit-native.spec-d.ts deleted file mode 100644 index db60274a..00000000 --- a/src/types/__tests__/omit-native.spec-d.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @file Type Tests - OmitNative - * @module tutils/types/tests/unit-d/OmitNative - */ - -import type Author from '#fixtures/author.interface' -import type TestSubject from '../omit-native' - -describe('unit-d:types/OmitNative', () => { - it('should equal typescript.Omit', () => { - // Arrange - type K = 'display_name' - - // Expect - expectTypeOf>().toEqualTypeOf> - }) -}) diff --git a/src/types/__tests__/omit.spec-d.ts b/src/types/__tests__/omit.spec-d.ts deleted file mode 100644 index 25e643db..00000000 --- a/src/types/__tests__/omit.spec-d.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file Type Tests - Omit - * @module tutils/types/tests/unit-d/Omit - */ - -import type Author from '#fixtures/author.interface' -import type Book from '#fixtures/book.interface' -import type EmptyObject from '../empty-object' -import type TestSubject from '../omit' -import type OmitNative from '../omit-native' - -describe('unit-d:types/Omit', () => { - it('should equal OmitNative if K extends keyof T', () => { - // Arrange - type T = Book - type K = 'publisher' | 'readers' - - // Expect - expectTypeOf>().toEqualTypeOf>() - }) - - it('should omit properties with respect for dot notation', () => { - // Arrange - type T = Omit & { display_name?: { value: string } } - type K = 'display_name.value' | 'email' - - // Expect - expectTypeOf>().toEqualTypeOf<{ - display_name?: EmptyObject - first_name: string - last_name: string - }>() - }) -}) diff --git a/src/types/__tests__/one-or-many.spec-d.ts b/src/types/__tests__/one-or-many.spec-d.ts index 302d27d8..4903f5e6 100644 --- a/src/types/__tests__/one-or-many.spec-d.ts +++ b/src/types/__tests__/one-or-many.spec-d.ts @@ -8,15 +8,16 @@ import type TestSubject from '../one-or-many' describe('unit-d:types/OneOrMany', () => { type T = string - it('should match T', () => { - expectTypeOf().toMatchTypeOf>() + it('should extract T', () => { + expectTypeOf>().extract().not.toBeNever() }) - it('should match T[]', () => { - expectTypeOf().toMatchTypeOf>() + it('should extract readonly T[]', () => { + expectTypeOf>().extract().not.toBeNever() }) - it('should match readonly T[]', () => { - expectTypeOf().toMatchTypeOf>() + it('should match T[]', () => { + expectTypeOf().toMatchTypeOf>() + expectTypeOf>().extract().toBeNever() }) }) diff --git a/src/types/__tests__/or-lowercase.spec-d.ts b/src/types/__tests__/or-lowercase.spec-d.ts index ca314765..35d2fa45 100644 --- a/src/types/__tests__/or-lowercase.spec-d.ts +++ b/src/types/__tests__/or-lowercase.spec-d.ts @@ -6,15 +6,13 @@ import type TestSubject from '../or-lowercase' describe('unit-d:types/OrLowercase', () => { - type T = 'CRLF' | 'LF' + type T = 'AND' | 'OR' it('should extract Lowercase', () => { - expectTypeOf>() - .extract>() - .toEqualTypeOf>() + expectTypeOf>().extract>().not.toBeNever() }) it('should extract T', () => { - expectTypeOf>().extract().toEqualTypeOf() + expectTypeOf>().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/or-uppercase.spec-d.ts b/src/types/__tests__/or-uppercase.spec-d.ts index 22839cd4..81c086c7 100644 --- a/src/types/__tests__/or-uppercase.spec-d.ts +++ b/src/types/__tests__/or-uppercase.spec-d.ts @@ -6,15 +6,13 @@ import type TestSubject from '../or-uppercase' describe('unit-d:types/OrUppercase', () => { - type T = 'crlf' | 'lf' + type T = 'and' | 'or' it('should extract T', () => { - expectTypeOf>().extract().toEqualTypeOf() + expectTypeOf>().extract().not.toBeNever() }) it('should extract Uppercase', () => { - expectTypeOf>() - .extract>() - .toEqualTypeOf>() + expectTypeOf>().extract>().not.toBeNever() }) }) diff --git a/src/types/__tests__/overwrite.spec-d.ts b/src/types/__tests__/overwrite.spec-d.ts index 0e36c415..2d533e18 100644 --- a/src/types/__tests__/overwrite.spec-d.ts +++ b/src/types/__tests__/overwrite.spec-d.ts @@ -3,17 +3,114 @@ * @module tutils/types/tests/unit-d/Overwrite */ +import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' +import type Person from '#fixtures/person.interface' +import type Vehicle from '#fixtures/vehicle' +import type Assign from '../assign' +import type EmptyArray from '../empty-array' +import type EmptyObject from '../empty-object' +import type OneOrMany from '../one-or-many' +import type { tag } from '../opaque' import type TestSubject from '../overwrite' describe('unit-d:types/Overwrite', () => { - type U = { name: string; uid: string } - type D = { uid: number } + it('should equal T if U is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if U is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('U extends ObjectCurly', () => { + type T = Author & { id?: string } + + it('should assign mutual properties of U to T', () => { + // Arrange + type U = { foo: string; readonly id: string } + type Expect = Assign> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if Keyof is never', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('U extends readonly ObjectCurly[]', () => { + type T = Book & { [tag]?: 'book'; id?: string } + + it('should equal T if U extends readonly EmptyObject[]', () => { + // Arrange + type U1 = (EmptyObject | {})[] + type U2 = [EmptyObject, EmptyObject, {}, {}] + type U3 = Readonly + type U4 = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('U extends readonly [infer H, ...infer R]', () => { + it('should assign mutual properties of U[i] to T', () => { + // Arrange + type U = [ + { readonly foo: string }, + { readonly [tag]?: 'book' }, + { readonly id?: string }, + { readonly publisher?: string }, + any, + never, + { readonly foo: string }, + { readonly [tag]: 'book' }, + { readonly id: string }, + { readonly publisher: string }, + { readonly readers: number } + ] + + // Expect + expectTypeOf>().toEqualTypeOf< + Omit & + U[7] & + U[8] & + U[9] & + U[10] + >() + }) + + it('should equal T if U extends Readonly', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>>().toEqualTypeOf() + }) + }) + + describe('number extends Length', () => { + it('should assign mutual properties of U[0] to T', () => { + // Arrange + type U = { foo: string; readonly id: string }[] + type Expect = Assign> + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) - it('should replace existing properties in U with properties in D', () => { - // Arrange - type Expected = { name: string; uid: number } + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Partial + type U = OneOrMany - // Expect - expectTypeOf>().toEqualTypeOf() + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/path.spec-d.ts b/src/types/__tests__/path.spec-d.ts index 12eb3a57..ed1a1da5 100644 --- a/src/types/__tests__/path.spec-d.ts +++ b/src/types/__tests__/path.spec-d.ts @@ -6,105 +6,384 @@ import type Author from '#fixtures/author.interface' import type Book from '#fixtures/book.interface' import type Publisher from '#fixtures/publisher.interface' +import type Vehicle from '#fixtures/vehicle' import type EmptyArray from '../empty-array' import type EmptyObject from '../empty-object' import type EmptyString from '../empty-string' import type Fn from '../fn' -import type Numeric from '../numeric' +import type Indices from '../indices' +import type Keyof from '../keyof' +import type NIL from '../nil' +import type Nilable from '../nilable' +import type NumberLike from '../number-like' +import type NumberString from '../number-string' +import type { tag } from '../opaque' import type TestSubject from '../path' -import type Timestamp from '../timestamp' +import type Stringify from '../stringify' describe('unit-d:types/Path', () => { - it('should equal never if T extends EmptyArray', () => { - expectTypeOf>().toBeNever() + type IndicesAll = + Indices extends infer I ? I | Stringify : never + + it('should equal NumberString if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() }) - it('should equal never if T extends EmptyObject', () => { - expectTypeOf>().toBeNever() + it('should equal never if T is unknown', () => { + expectTypeOf>().toBeNever() }) - it('should equal never if T extends EmptyString', () => { - expectTypeOf>().toBeNever() + describe('T extends ObjectCurly', () => { + it('should construct union of property paths', () => { + // Arrange + type T = Publisher & { readonly id: 'publisher' } + + // Expect + expectTypeOf>().toEqualTypeOf< + | keyof T + | `display_name.${keyof T['display_name']}` + | `display_name.value.${Stringify}` + | `display_name.value.length.${keyof number}` + | `email.${Stringify}` + | `email.length.${keyof number}` + | `id.${Exclude, NumberLike>}` + | `id.${Indices}` + | `id.length.${keyof number}` + | `name.${Stringify}` + | `name.length.${keyof number}` + >() + }) + + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + + describe('E extends true', () => { + type E = true + + it('should construct union of enumerable property paths', () => { + // Arrange + type T = Book & { readonly id: 'book' } + + // Expect + expectTypeOf>().toEqualTypeOf< + | keyof T + | `authors.${Indices}.${keyof T['authors'][0]}` + | `authors.${Indices}.display_name.${Indices}` + | `authors.${Indices}.email.${Indices}` + | `authors.${Indices}.first_name.${Indices}` + | `authors.${Indices}.last_name.${Indices}` + | `authors.${Indices}` + | `id.${Indices}` + | `publisher.${keyof Publisher}` + | `publisher.display_name.${keyof Publisher['display_name']}` + | `publisher.display_name.value.${Indices}` + | `publisher.email.${Indices}` + | `publisher.name.${Indices}` + | `title.${Indices}` + >() + }) + + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + }) }) - it('should equal union if T extends ObjectCurly', () => { - // Arrange - type T = Omit & { - authors: Author[] - fn?: Fn & { id: number & { tag: 'book' } } - publisher: Omit & { - display_name?: { value: string } - fn?: Fn & { id: number & { tag: 'publisher' } } - } - } - - // Expect - expectTypeOf>().toEqualTypeOf< - | 'authors' - | 'fn.id.tag' - | 'fn.id' - | 'fn' - | 'isbn' - | 'publisher.display_name.value' - | 'publisher.display_name' - | 'publisher.email' - | 'publisher.fn.id.tag' - | 'publisher.fn.id' - | 'publisher.fn' - | 'publisher.name' - | 'publisher' - | 'readers' - | 'title' - | `authors.${number}.display_name` - | `authors.${number}.email` - | `authors.${number}.first_name` - | `authors.${number}.last_name` - | `authors.${number}` - >() + describe('T extends Primitive', () => { + it('should equal never if Keyof is never', () => { + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + expectTypeOf>().toBeNever() + }) + + describe('T extends bigint', () => { + it('should construct union of property paths', () => { + // Arrange + type Expect = 'toLocaleString' | 'toString' | 'valueOf' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('E extends true', () => { + it('should construct union of enumerable property paths', () => { + // Arrange + type T = bigint & { [tag]: 'bigint'; readonly id: 'bigint' } + + // Expect + expectTypeOf>().toEqualTypeOf< + 'id' | `id.${Indices}` + >() + }) + }) + }) + + describe('T extends boolean', () => { + it('should construct union of property paths', () => { + // Arrange + type T = boolean + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('E extends true', () => { + it('should construct union of enumerable property paths', () => { + // Arrange + type T = boolean & { [tag]: 'boolean'; readonly id: 'boolean' } + type Expect = 'id' | `id.${Indices}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends number', () => { + it('should construct union of property paths', () => { + // Arrange + type T = number + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + describe('E extends true', () => { + it('should construct union of enumerable property paths', () => { + // Arrange + type T = number & { [tag]: 'number'; readonly id: 'number' } + type Expect = 'id' | `id.${Indices}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + describe('IsLiteral> extends true', () => { + it('should construct union of property paths', () => { + // Arrange + type T = 'book' + + // Expect + expectTypeOf>().toEqualTypeOf< + | IndicesAll + | Stringify> + | `length.${keyof number}` + >() + }) + + describe('E extends true', () => { + type E = true + + it('should equal Indices', () => { + // Arrange + type T1 = EmptyString + type T2 = 'book' + + // Expect + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf>() + }) + }) + }) + + describe('number extends Indices', () => { + it('should construct union of property paths', () => { + // Arrange + type T = string + + // Expect + expectTypeOf>().toEqualTypeOf< + | IndicesAll + | Stringify> + | `length.${keyof number}` + >() + }) + + describe('E extends true', () => { + type E = true + + it('should construct union of enumerable property paths', () => { + // Arrange + type T = string & { [tag]: 'string'; readonly id: 'string' } + type Expect = IndicesAll | 'id' | `id.${Indices}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends symbol', () => { + it('should construct union of property paths', () => { + expectTypeOf>().toEqualTypeOf< + | 'description' + | 'toString' + | 'valueOf' + | `description.${Stringify}` + | `description.length.${keyof number}` + >() + }) + + describe('E extends true', () => { + it('should construct union of enumerable property paths', () => { + // Arrange + type T = symbol & { [tag]: 'symbol'; readonly id: 'symbol' } + type Expect = 'id' | `id.${Indices}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) }) - it('should equal union if T extends Readonly', () => { - // Arrange - type F1 = Fn - type F2Obj = { '0': { '1': { '2': { '3': { '4': { '5': number } } } } } } - type F2 = Readonly - type E1 = never - type E2 = '0.1.2.3.4.5' | '0.1.2.3.4' | '0.1.2.3' | '0.1.2' | '0.1' | '0' - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + describe('T extends Readonly', () => { + it('should construct union of property paths', () => { + expectTypeOf>>().toEqualTypeOf< + | 'apply' + | 'arguments' + | 'bind' + | 'call' + | 'caller' + | 'length' + | 'name' + | 'prototype' + | 'toString' + | `length.${keyof number}` + | `name.${Stringify}` + | `name.length.${keyof number}` + >() + }) + + describe('E extends true', () => { + it('should construct union of enumerable property paths', () => { + // Arrange + type T = Readonly & { [tag]: 'fn'; readonly id: 'fn' } + type Expect = 'id' | `id.${Indices}` + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) }) - it('should equal union if T extends Readonly', () => { - // Arrange - type P1 = boolean - type P2 = string & { tag: string } - type P3 = Readonly - type P4 = Timestamp<'unix'> - - // Expect - expectTypeOf>().toBeNever() - expectTypeOf>().toEqualTypeOf<'tag'>() - expectTypeOf>().toEqualTypeOf<'tag'>() - expectTypeOf>().toBeNever() + describe('T extends readonly unknown[]', () => { + describe('IsLiteral> extends true', () => { + it('should construct union of property paths', () => { + // Arrange + type T = [Author, Author?] + + // Expect + expectTypeOf>().toEqualTypeOf< + | IndicesAll + | Stringify> + | `${Indices}.${keyof T[0]}` + | `${Indices}.display_name.${Stringify}` + | `${Indices}.display_name.length.${keyof number}` + | `${Indices}.email.${Stringify}` + | `${Indices}.email.length.${keyof number}` + | `${Indices}.first_name.${Stringify}` + | `${Indices}.first_name.length.${keyof number}` + | `${Indices}.last_name.${Stringify}` + | `${Indices}.last_name.length.${keyof number}` + | `length.${keyof number}` + >() + }) + + describe('E extends true', () => { + type E = true + + it('should construct union of enumerable property paths', () => { + // Arrange + type T = ['author', Author] + + // Expect + expectTypeOf>().toEqualTypeOf< + | IndicesAll + | `${-1 | 1}.display_name.${Indices}` + | `${-1 | 1}.display_name` + | `${-1 | 1}.email.${Indices}` + | `${-1 | 1}.email` + | `${-1 | 1}.first_name.${Indices}` + | `${-1 | 1}.first_name` + | `${-1 | 1}.last_name.${Indices}` + | `${-1 | 1}.last_name` + | `${-2 | 0}.${Indices}` + >() + }) + + it('should equal never if T extends Readonly', () => { + expectTypeOf>().toBeNever() + expectTypeOf, E>>().toBeNever() + }) + }) + }) + + describe('number extends Indices', () => { + it('should construct union of property paths', () => { + // Arrange + type T = Author[] + + // Expect + expectTypeOf>().toEqualTypeOf< + | IndicesAll + | Stringify> + | `${Indices}.${keyof T[0]}` + | `${Indices}.display_name.${Stringify}` + | `${Indices}.display_name.length.${keyof number}` + | `${Indices}.email.${Stringify}` + | `${Indices}.email.length.${keyof number}` + | `${Indices}.first_name.${Stringify}` + | `${Indices}.first_name.length.${keyof number}` + | `${Indices}.last_name.${Stringify}` + | `${Indices}.last_name.length.${keyof number}` + | `length.${keyof number}` + >() + }) + + describe('E extends true', () => { + it('should construct union of enumerable property paths', () => { + // Arrange + type T = Author[] & { [tag]: 'Author[]'; readonly id: 'Author[]' } + + // Expect + expectTypeOf>().toEqualTypeOf< + | IndicesAll + | 'id' + | `${Indices}.${keyof T[0]}` + | `${Indices}.display_name.${Indices}` + | `${Indices}.email.${Indices}` + | `${Indices}.first_name.${Indices}` + | `${Indices}.last_name.${Indices}` + | `id.${Indices}` + >() + }) + }) + }) }) - it('should equal union if T extends readonly unknown[]', () => { - // Arrange - type A1 = Author[] & { matcher: RegExp } - type A2 = { matcher: RegExp } & [Author] - type E1 = Numeric | 'matcher' | `${Numeric}.${keyof Author}` - type E2 = - | '0.display_name' - | '0.email' - | '0.first_name' - | '0.last_name' - | '0' - | 'matcher' - - // Expect - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable + + // Expect + expectTypeOf>().toEqualTypeOf< + | Keyof + | `${keyof Author}.${Indices}` + | `${keyof Omit}.${Indices}` + >() + }) }) }) diff --git a/src/types/__tests__/pick-native.spec-d.ts b/src/types/__tests__/pick-native.spec-d.ts deleted file mode 100644 index db2f1b43..00000000 --- a/src/types/__tests__/pick-native.spec-d.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @file Type Tests - PickNative - * @module tutils/types/tests/unit-d/PickNative - */ - -import type Author from '#fixtures/author.interface' -import type TestSubject from '../pick-native' - -describe('unit-d:types/PickNative', () => { - it('should equal typescript.Pick', () => { - // Arrange - type K = 'email' - - // Expect - expectTypeOf>().toEqualTypeOf> - }) -}) diff --git a/src/types/__tests__/pick.spec-d.ts b/src/types/__tests__/pick.spec-d.ts deleted file mode 100644 index ca6e9f25..00000000 --- a/src/types/__tests__/pick.spec-d.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file Type Tests - Pick - * @module tutils/types/tests/unit-d/Pick - */ - -import type Author from '#fixtures/author.interface' -import type Book from '#fixtures/book.interface' -import type Publisher from '#fixtures/publisher.interface' -import type TestSubject from '../pick' -import type PickNative from '../pick-native' - -describe('unit-d:types/Pick', () => { - it('should equal PickNative if K extends keyof T', () => { - // Arrange - type T = Book - type K = 'isbn' | 'title' - - // Expect - expectTypeOf>().toEqualTypeOf>() - }) - - it('should pick properties with respect for dot notation', () => { - // Arrange - type K = - | 'authors.0.display_name' - | 'authors.0.email' - | 'isbn' - | 'publisher.name' - | 'title' - - // Expect - expectTypeOf>().toEqualTypeOf<{ - authors: { - display_name?: Author['display_name'] - email?: Exclude - }[] - publisher?: PickNative - isbn: Book['isbn'] - title: Book['title'] - }>() - }) -}) diff --git a/src/types/__tests__/primitive.spec-d.ts b/src/types/__tests__/primitive.spec-d.ts index 4687f1dd..3e8f0959 100644 --- a/src/types/__tests__/primitive.spec-d.ts +++ b/src/types/__tests__/primitive.spec-d.ts @@ -8,20 +8,18 @@ import type TestSubject from '../primitive' describe('unit-d:types/Primitive', () => { it('should extract JsonPrimitive', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract bigint', () => { - expectTypeOf().extract().toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract symbol', () => { - expectTypeOf().extract().toBeSymbol() + expectTypeOf().extract().not.toBeNever() }) it('should extract undefined', () => { - expectTypeOf().extract().toBeUndefined() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/promisable.spec-d.ts b/src/types/__tests__/promisable.spec-d.ts index 1cdfcfcc..11c80b5f 100644 --- a/src/types/__tests__/promisable.spec-d.ts +++ b/src/types/__tests__/promisable.spec-d.ts @@ -9,12 +9,10 @@ describe('unit-d:types/Promisable', () => { type T = string it('should extract PromiseLike', () => { - expectTypeOf>() - .extract>() - .toEqualTypeOf>() + expectTypeOf>().extract>().not.toBeNever() }) it('should extract T', () => { - expectTypeOf>().extract().toEqualTypeOf() + expectTypeOf>().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/property-key.spec-d.ts b/src/types/__tests__/property-key.spec-d.ts index 5c890301..71c48259 100644 --- a/src/types/__tests__/property-key.spec-d.ts +++ b/src/types/__tests__/property-key.spec-d.ts @@ -8,12 +8,10 @@ import type TestSubject from '../property-key' describe('unit-d:types/PropertyKey', () => { it('should extract NumberString', () => { - expectTypeOf() - .extract() - .toEqualTypeOf() + expectTypeOf().extract().not.toBeNever() }) it('should extract symbol', () => { - expectTypeOf().extract().toBeSymbol() + expectTypeOf().extract().not.toBeNever() }) }) diff --git a/src/types/__tests__/range-natural.spec-d.ts b/src/types/__tests__/range-natural.spec-d.ts index ea82fe13..b7029577 100644 --- a/src/types/__tests__/range-natural.spec-d.ts +++ b/src/types/__tests__/range-natural.spec-d.ts @@ -6,12 +6,18 @@ import type TestSubject from '../range-natural' describe('unit-d:types/NaturalRange', () => { - it('should equal union of numbers in the range [Acc["length"],Max)', () => { - // Arrange - type Max = 3 + it('should construct union of numbers in the range [Acc["length"],M)', () => { + expectTypeOf>().toEqualTypeOf<0 | 1 | 2>() + expectTypeOf>().toEqualTypeOf<1 | 2 | 3>() + }) + + it('should equal never if M extends 0', () => { + expectTypeOf>().toBeNever() + }) - // Expect - expectTypeOf>().toEqualTypeOf<0 | 1 | 2>() - expectTypeOf>().toEqualTypeOf<1 | 2>() + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf<0 | 1>() + }) }) }) diff --git a/src/types/__tests__/reverse.spec-d.ts b/src/types/__tests__/reverse.spec-d.ts new file mode 100644 index 00000000..f08cb397 --- /dev/null +++ b/src/types/__tests__/reverse.spec-d.ts @@ -0,0 +1,115 @@ +/** + * @file Type Tests - Reverse + * @module tutils/types/tests/unit-d/Reverse + */ + +import type EmptyArray from '../empty-array' +import type EmptyString from '../empty-string' +import type TestSubject from '../reverse' + +describe('unit-d:types/Reverse', () => { + it('should equal any[] if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + it('should equal T if Length> extends 0', () => { + // Arrange + type T1 = EmptyArray + type T2 = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) + + it('should equal T if Length> extends 1', () => { + // Arrange + type T1 = ['a'] + type T2 = readonly ['z'?] + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf().toEqualTypeOf() + }) + + it('should reverse T', () => { + // Arrange + type T1 = readonly ['a', 'b', 'c', 'd', 'e'] + type T2 = readonly ['a'?, 'b'?, 'c'?, 'd'?, 'e'?] + // prettier-ignore + type T3 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 't', 'u', 'v', 'w', 'x', 'y', 'z'?] + type E1 = ['e', 'd', 'c', 'b', 'a'] + type E2 = ['e'?, 'd'?, 'c'?, 'b'?, 'a'?] + // prettier-ignore + type E3 = ['z' | undefined, 'y', 'x', 'w', 'v', 'u', 't', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'z', 'y', 'x', 'w', 'v', 'u', 't', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', 'z', 'y', 'x', 'w', 'v', 'u', 't', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends Length', () => { + it('should equal T', () => { + // Arrange + type T1 = string[] + type T2 = readonly string[] + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + describe('IsLiteral> extends true', () => { + it('should equal T if Length extends 0', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal T if Length extends 1', () => { + // Arrange + type T = 'z' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should reverse T', () => { + // Arrange + type T = 'abcdefghijklmnopqrstuvwxyz' + type Expect = 'zyxwvutsrqponmlkjihgfedcba' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('number extends Length', () => { + it('should equal T', () => { + expectTypeOf>().toBeString() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + // prettier-ignore + type T = readonly ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 't', 'u', 'v', 'w', 'x', 'y', 'z'?] | 'abcdefghijklmnopqrstuvwxyz' + // prettier-ignore + type Expect = 'zyxwvutsrqponmlkjihgfedcba' | ['z' | undefined, 'y', 'x', 'w', 'v', 'u', 't', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/__tests__/sift.spec-d.ts b/src/types/__tests__/sift.spec-d.ts index 6743eef1..2480292e 100644 --- a/src/types/__tests__/sift.spec-d.ts +++ b/src/types/__tests__/sift.spec-d.ts @@ -4,26 +4,51 @@ */ import type EmptyArray from '../empty-array' -import type EmptyString from '../empty-string' -import type NIL from '../nil' +import type Falsy from '../falsy' +import type Nilable from '../nilable' import type TestSubject from '../sift' describe('unit-d:types/Sift', () => { - type I = EmptyString | NIL | 0 | 0n | 1 | 1n | false + it('should equal EmptyArray if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal EmptyArray if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) - it('should equal T if T extends EmptyArray', () => { - expectTypeOf>().toEqualTypeOf() + it('should equal T if Falsy does not extend T[number]', () => { + // Arrange + type T = ['a', 'b', 'c'] + + // Expect + expectTypeOf>().toEqualTypeOf() }) - it('should filter Falsy out of T', () => { + it('should equal T if T extends Readonly', () => { // Arrange - type T1 = [1, 2, 3] - type T2 = [I] - type T3 = I[] + type T1 = EmptyArray + type T2 = Readonly // Expect expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf<[1, 1n]>() - expectTypeOf>().toEqualTypeOf<(1 | 1n)[]>() + expectTypeOf>().toEqualTypeOf() + }) + + describe('Falsy extends T[number]', () => { + it('should exclude Falsy from T[number]', () => { + expectTypeOf>().toEqualTypeOf<13[]>() + expectTypeOf>().toEqualTypeOf<[1, 1n]>() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable[] | [0, 1] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/simplify.spec-d.ts b/src/types/__tests__/simplify.spec-d.ts index aecddbe6..edbb07cf 100644 --- a/src/types/__tests__/simplify.spec-d.ts +++ b/src/types/__tests__/simplify.spec-d.ts @@ -3,13 +3,25 @@ * @module tutils/types/tests/unit-d/Simplify */ -import type { IOrderedPair, TOrderedPair } from '#fixtures/ordered-pair' +import type Author from '#fixtures/author.interface' +import type Book from '#fixtures/book.interface' import type ObjectPlain from '../object-plain' import type TestSubject from '../simplify' describe('unit-d:types/Simplify', () => { it('should simplify complex type', () => { - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toMatchTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toMatchTypeOf() + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Author | Book + + // Expect + expectTypeOf>().toBeNever() + expectTypeOf>().toMatchTypeOf() + }) }) }) diff --git a/src/types/__tests__/split.spec-d.ts b/src/types/__tests__/split.spec-d.ts index a1e2f879..fcf5fc7a 100644 --- a/src/types/__tests__/split.spec-d.ts +++ b/src/types/__tests__/split.spec-d.ts @@ -3,23 +3,174 @@ * @module tutils/types/tests/unit-d/Split */ +import type { Sep } from '@flex-development/pathe' +import type Dot from '../dot' import type EmptyArray from '../empty-array' import type EmptyString from '../empty-string' +import type Optional from '../optional' import type TestSubject from '../split' describe('unit-d:types/Split', () => { - it('should equal T.split(Delimiter)', () => { - // Arrange - type T1 = EmptyString - type T2 = 'publisher.email' - - // Expect - expectTypeOf>().toEqualTypeOf<[T1]>() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf<[T1]>() - expectTypeOf>().toEqualTypeOf<[T2]>() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf<['publisher', 'email']>() + it('should equal EmptyArray if T is never', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal string[] if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('IsLiteral extends true', () => { + describe('S extends RegExp', () => { + it('should equal string[]', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('S extends string', () => { + it('should equal T.split(S)', () => { + // Arrange + type T1 = EmptyString + type T2 = Lowercase<'0.x.y'> + + // Expect + expectTypeOf>().toEqualTypeOf<[T1]>() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf<[T2]>() + expectTypeOf>().toEqualTypeOf<['0', 'x', 'y']>() + }) + + describe('string extends S', () => { + it('should equal string[]', () => { + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('S extends undefined', () => { + it('should equal [T]', () => { + // Arrange + type T1 = EmptyString + type T2 = 'a.b.c' + + // Expect + expectTypeOf>().toEqualTypeOf<[T1]>() + expectTypeOf>().toEqualTypeOf<[T2]>() + }) + }) + }) + + describe('T extends object', () => { + type T = 'data.vehicle' & { id: string } + + it('should equal string[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('S extends undefined', () => { + it('should equal [T]', () => { + expectTypeOf>().toEqualTypeOf<[T]>() + }) + }) + }) + + describe('intrinsics', () => { + describe('Capitalize extends T', () => { + type T = Capitalize + + it('should equal string[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('S extends undefined', () => { + it('should equal [T]', () => { + expectTypeOf>().toEqualTypeOf<[T]>() + }) + }) + }) + + describe('Lowercase extends T', () => { + type T = Lowercase + + it('should equal T[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('S extends undefined', () => { + it('should equal [T]', () => { + expectTypeOf>().toEqualTypeOf<[T]>() + }) + }) + }) + + describe('Uncapitalize extends T', () => { + type T = Uncapitalize + + it('should equal string[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('S extends undefined', () => { + it('should equal [T]', () => { + expectTypeOf>().toEqualTypeOf<[T]>() + }) + }) + }) + + describe('Uppercase extends T', () => { + type T = Uppercase + + it('should equal T[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('S extends undefined', () => { + it('should equal [T]', () => { + expectTypeOf>().toEqualTypeOf<[T]>() + }) + }) + }) + }) + + describe('string extends T', () => { + type T = string + + it('should equal string[]', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('S extends undefined', () => { + it('should equal [T]', () => { + expectTypeOf>().toEqualTypeOf<[T]>() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Lowercase | 'a.b.c' | 'abc' + type S = Optional + + // Expect + expectTypeOf>().toEqualTypeOf< + | Lowercase[] + | ['a.b.c'] + | ['a', '.', 'b', '.', 'c'] + | ['a', 'b', 'c'] + | ['abc'] + >() + }) }) }) diff --git a/src/types/__tests__/stringafiable.spec-d.ts b/src/types/__tests__/stringafiable.spec-d.ts index 3fe99892..9b0f12f9 100644 --- a/src/types/__tests__/stringafiable.spec-d.ts +++ b/src/types/__tests__/stringafiable.spec-d.ts @@ -6,7 +6,7 @@ import type TestSubject from '../stringafiable' describe('unit-d:types/Stringafiable', () => { - it('should match [toString(): string]', () => { + it('should match [toString: () => string]', () => { expectTypeOf() .toHaveProperty('toString') .toEqualTypeOf<() => string>() diff --git a/src/types/__tests__/subtract.spec-d.ts b/src/types/__tests__/subtract.spec-d.ts new file mode 100644 index 00000000..0a7fdb29 --- /dev/null +++ b/src/types/__tests__/subtract.spec-d.ts @@ -0,0 +1,13 @@ +/** + * @file Type Tests - Subtract + * @module tutils/types/tests/unit-d/Subtract + */ + +import type TestSubject from '../subtract' + +describe('unit-d:types/Subtract', () => { + it('should equal difference between M and S', () => { + expectTypeOf>().toEqualTypeOf<3>() + expectTypeOf>().toEqualTypeOf<0>() + }) +}) diff --git a/src/types/__tests__/tail.spec-d.ts b/src/types/__tests__/tail.spec-d.ts index df3b1b97..7cb5e60f 100644 --- a/src/types/__tests__/tail.spec-d.ts +++ b/src/types/__tests__/tail.spec-d.ts @@ -3,19 +3,81 @@ * @module tutils/types/tests/unit-d/Tail */ -import type Digit from '../digit' +import type Dot from '../dot' +import type EmptyArray from '../empty-array' +import type EmptyString from '../empty-string' import type TestSubject from '../tail' describe('unit-d:types/Tail', () => { - it('should equal T.slice(1) if T extends readonly unknown[]', () => { - expectTypeOf>().toEqualTypeOf<[2, 3]>() + it('should equal never if T is any', () => { + expectTypeOf>().toBeNever() }) - it('should equal T.slice(T.indexOf(D)) if T extends string', () => { - expectTypeOf>().toEqualTypeOf<'foo.bar'>() + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() }) - it('should equal never if T is not an array or string', () => { - expectTypeOf>().toBeNever() + describe('T extends readonly unknown[]', () => { + it('should equal T if T extends Readonly', () => { + // Arrange + type T1 = EmptyArray + type T2 = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends readonly [T[0]?, ...infer R]', () => { + it('should equal R', () => { + // Arrange + type T1 = ['x'] + type T2 = ['x'?] + type T3 = ['x', 'y', 'z'] + type T4 = ['x', 'y', 'z'?] + type T5 = ['x'?, 'y'?, 'z'?] + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf<['y', 'z']>() + expectTypeOf>().toEqualTypeOf<['y', 'z'?]>() + expectTypeOf>().toEqualTypeOf<['y'?, 'z'?]>() + }) + }) + }) + + describe('T extends string', () => { + it('should equal T if T extends EmptyString', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends `${string}${D}${infer R}`', () => { + it('should equal R', () => { + expectTypeOf>().toEqualTypeOf<'bc'>() + expectTypeOf>().toEqualTypeOf<'b.c'>() + }) + }) + + describe('string extends T', () => { + it('should equal T', () => { + // Arrange + type T = string + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = 'data.message' | [number] + type Expect = EmptyArray | 'message' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) }) }) diff --git a/src/types/__tests__/template-data.spec-d.ts b/src/types/__tests__/template-data.spec-d.ts new file mode 100644 index 00000000..6a0d3c48 --- /dev/null +++ b/src/types/__tests__/template-data.spec-d.ts @@ -0,0 +1,10 @@ +/** + * @file Type Tests - TemplateData + * @module tutils/types/tests/unit-d/TemplateData + */ + +// import type TestSubject from '../template-data' + +describe.todo('unit-d:types/TemplateData', () => { + // +}) diff --git a/src/types/__tests__/timestamp.spec-d.ts b/src/types/__tests__/timestamp.spec-d.ts index 0c228f80..8ec0bca4 100644 --- a/src/types/__tests__/timestamp.spec-d.ts +++ b/src/types/__tests__/timestamp.spec-d.ts @@ -20,10 +20,10 @@ describe('unit-d:types/Timestamp', () => { it('should match [readonly [tag]: TimestampToken]', () => { // Arrange - type Expected = { readonly [tag]: TimestampToken } + type Expect = { readonly [tag]: TimestampToken } // Expect - expectTypeOf>().toMatchTypeOf() + expectTypeOf>().toMatchTypeOf() }) describe('F extends "iso"', () => { diff --git a/src/types/__tests__/trim-end.spec-d.ts b/src/types/__tests__/trim-end.spec-d.ts index fa70e109..31c9280a 100644 --- a/src/types/__tests__/trim-end.spec-d.ts +++ b/src/types/__tests__/trim-end.spec-d.ts @@ -6,7 +6,25 @@ import type TestSubject from '../trim-end' describe('unit-d:types/TrimEnd', () => { - it('should return T without trailing whitespaces', () => { + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal string if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should remove trailing whitespaces from T', () => { expectTypeOf>().toEqualTypeOf<' foo'>() }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = '0 ' | '1 ' | '2 ' + + // Expect + expectTypeOf>().toEqualTypeOf<'0' | '1' | '2'>() + }) + }) }) diff --git a/src/types/__tests__/trim-start.spec-d.ts b/src/types/__tests__/trim-start.spec-d.ts index 2f03e93b..1fe2751f 100644 --- a/src/types/__tests__/trim-start.spec-d.ts +++ b/src/types/__tests__/trim-start.spec-d.ts @@ -6,7 +6,25 @@ import type TestSubject from '../trim-start' describe('unit-d:types/TrimStart', () => { - it('should return T without leading whitespaces', () => { + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal string if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should removing leading whitespaces from T', () => { expectTypeOf>().toEqualTypeOf<'bar '>() }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = ' 0' | ' 1' | ' 2' + + // Expect + expectTypeOf>().toEqualTypeOf<'0' | '1' | '2'>() + }) + }) }) diff --git a/src/types/__tests__/trim.spec-d.ts b/src/types/__tests__/trim.spec-d.ts index 1f84a8b6..c393ed1d 100644 --- a/src/types/__tests__/trim.spec-d.ts +++ b/src/types/__tests__/trim.spec-d.ts @@ -6,7 +6,25 @@ import type TestSubject from '../trim' describe('unit-d:types/Trim', () => { - it('should return T without leading and trailing whitespaces', () => { + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal string if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should remove leading and trailing whitespaces from T', () => { expectTypeOf>().toEqualTypeOf<'baz'>() }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = ' 0 ' | ' 1 ' | ' 2 ' + + // Expect + expectTypeOf>().toEqualTypeOf<'0' | '1' | '2'>() + }) + }) }) diff --git a/src/types/__tests__/tuple-length.spec-d.ts b/src/types/__tests__/tuple-length.spec-d.ts deleted file mode 100644 index 903e901d..00000000 --- a/src/types/__tests__/tuple-length.spec-d.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file Type Tests - TupleLength - * @module tutils/types/tests/unit-d/TupleLength - */ - -import type Author from '#fixtures/author.interface' -import type EmptyArray from '../empty-array' -import type TestSubject from '../tuple-length' - -describe('unit-d:types/TupleLength', () => { - it('should equal T["length"] if T is a tuple', () => { - expectTypeOf>().toEqualTypeOf<0>() - expectTypeOf>().toEqualTypeOf<1>() - expectTypeOf>().toEqualTypeOf<2>() - }) - - it('should equal number if T is not a tuple', () => { - expectTypeOf>().toBeNumber() - }) -}) diff --git a/src/types/__tests__/tuple-to-union.spec-d.ts b/src/types/__tests__/tuple-to-union.spec-d.ts deleted file mode 100644 index 1229b7dd..00000000 --- a/src/types/__tests__/tuple-to-union.spec-d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file Type Tests - TupleToUnion - * @module tutils/types/tests/unit-d/TupleToUnion - */ - -import type TestSubject from '../tuple-to-union' - -describe('unit-d:types/TupleToUnion', () => { - it('should equal tuple elements union if T is a tuple', () => { - expectTypeOf>().toEqualTypeOf<0 | 1 | 2>() - }) - - it('should equal never if T is not a tuple', () => { - expectTypeOf>().toBeNever() - }) -}) diff --git a/src/types/__tests__/union-to-intersection.spec-d.ts b/src/types/__tests__/union-to-intersection.spec-d.ts index 5499e81c..2cc3c329 100644 --- a/src/types/__tests__/union-to-intersection.spec-d.ts +++ b/src/types/__tests__/union-to-intersection.spec-d.ts @@ -6,14 +6,21 @@ import type TestSubject from '../union-to-intersection' describe('unit-d:types/UnionToIntersection', () => { - it('should convert U to intersection', () => { + it('should convert T to intersection if T is a union', () => { // Arrange - type U1 = number - type U2 = { first_name: string } | { last_name: string } - type E1 = U1 - type E2 = { first_name: string } & { last_name: string } + type T = { x: 0 } | { x: 1 } - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + // Expect + expectTypeOf>().toEqualTypeOf<{ x: 0 } & { x: 1 }>() + }) + + it('should equal T if T is not a union', () => { + expectTypeOf>().toBeAny() + expectTypeOf>().toBeNumber() + expectTypeOf>().toBeUnknown() + }) + + it('should equal unknown if T is never', () => { + expectTypeOf>().toBeUnknown() }) }) diff --git a/src/types/__tests__/union-to-tuple.spec-d.ts b/src/types/__tests__/union-to-tuple.spec-d.ts index a7e452a2..9a35f3f8 100644 --- a/src/types/__tests__/union-to-tuple.spec-d.ts +++ b/src/types/__tests__/union-to-tuple.spec-d.ts @@ -7,10 +7,6 @@ import type TestSubject from '../union-to-tuple' describe('unit-d:types/UnionToTuple', () => { it('should convert U to tuple', () => { - // Arrange - type U = 'x' | 'y' | 'z' - - // Expect - expectTypeOf>().toEqualTypeOf<['x', 'y', 'z']>() + expectTypeOf>().toEqualTypeOf<['x']>() }) }) diff --git a/src/types/__tests__/unwrap-numeric.spec-d.ts b/src/types/__tests__/unwrap-numeric.spec-d.ts new file mode 100644 index 00000000..4e267983 --- /dev/null +++ b/src/types/__tests__/unwrap-numeric.spec-d.ts @@ -0,0 +1,49 @@ +/** + * @file Type Tests - UnwrapNumeric + * @module tutils/types/tests/unit-d/UnwrapNumeric + */ + +import type NumberLike from '../number-like' +import type Numeric from '../numeric' +import type NegativeNumeric from '../numeric-negative' +import type TestSubject from '../unwrap-numeric' + +describe('unit-d:types/UnwrapNumeric', () => { + it('should equal never if T is never', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal never if T is unknown', () => { + expectTypeOf>().toBeNever() + }) + + it('should equal number if NegativeNumeric extends T', () => { + expectTypeOf>().toEqualTypeOf() + }) + + it('should equal number if T is any', () => { + expectTypeOf>().toEqualTypeOf() + }) + + describe('T extends `-${infer N extends 0}`', () => { + it('should equal N', () => { + expectTypeOf>().toEqualTypeOf<0>() + }) + }) + + describe('T extends `${infer N extends number}`', () => { + it('should equal N', () => { + expectTypeOf>().toEqualTypeOf<-3>() + expectTypeOf>().toEqualTypeOf<3>() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + expectTypeOf>().toEqualTypeOf<-3 | 3>() + expectTypeOf>().toEqualTypeOf<0 | 1 | 2>() + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/array-to-union.ts b/src/types/array-to-union.ts new file mode 100644 index 00000000..43e22a41 --- /dev/null +++ b/src/types/array-to-union.ts @@ -0,0 +1,35 @@ +/** + * @file Type Definitions - ArrayToUnion + * @module tutils/types/ArrayToUnion + */ + +import type IfAny from './if-any' +import type Nilable from './nilable' + +/** + * Converts array `T` to a [union][1]. + * + * [1]: https://www.typescriptlang.org/docs/handbook/unions-and-intersections#union-types + * + * @example + * type X = ArrayToUnion<['a', 'b', 'c']> + * // 'a' | 'b' | 'c' + * @example + * type X = ArrayToUnion<['a', 'b', 'c'?]> + * // 'a' | 'b' | 'c' | undefined + * @example + * type X = ArrayToUnion> + * // never + * @example + * type X = ArrayToUnion + * // never + * @example + * type X = ArrayToUnion + * // never + * + * @template T - Type to evaluate + */ +type ArrayToUnion> = + T extends readonly unknown[] ? IfAny : never + +export type { ArrayToUnion as default } diff --git a/src/types/assign.ts b/src/types/assign.ts index 21681ece..689cd737 100644 --- a/src/types/assign.ts +++ b/src/types/assign.ts @@ -3,34 +3,78 @@ * @module tutils/types/Assign */ -import type EmptyArray from './empty-array' import type EmptyObject from './empty-object' -import type Head from './head' -import type Merge from './merge' +import type HasKeys from './has-keys' +import type IsAny from './is-any' +import type IsEqual from './is-equal' +import type IsNever from './is-never' +import type Nilable from './nilable' import type ObjectCurly from './object-curly' +import type OmitIndexSignature from './omit-index-signature' import type OneOrMany from './one-or-many' +import type PropertyKey from './property-key' import type Simplify from './simplify' /** - * Merges one or more source objects into target object `T`. + * Assigns properties of `H` to `T`. * - * Source objects are applied from left to right. Properties of rightmost - * sources override those of leftmost sources. + * @internal + * + * @template T - Target object + * @template H - Source object + */ +type Assigner = IsAny extends true + ? Simplify & T> + : IsNever extends true + ? T + : HasKeys extends true + ? IsEqual extends true + ? T + : H extends unknown + ? { + [K in keyof (H & { + [K in keyof T as K extends keyof OmitIndexSignature ? never : K]: K + })]: K extends keyof OmitIndexSignature + ? K extends keyof H + ? H[K] + : never + : K extends keyof OmitIndexSignature + ? T[K] + : K extends keyof H + ? H[K] + : K extends keyof T + ? T[K] + : never + } + : T + : T + +/** + * Assigns properties of one or more source objects to target object `T`. + * + * Source objects are applied from left to right. Subsequent sources overwrite + * property assignments of previous sources. + * + * @todo examples * * @template T - Target object * @template U - Source object or source object array */ type Assign< - T extends ObjectCurly, + T extends Nilable, U extends OneOrMany = EmptyObject -> = U extends EmptyArray | EmptyObject - ? T - : U extends ObjectCurly - ? Merge - : Simplify< - U extends [infer H, ...infer Rest extends readonly ObjectCurly[]] - ? Assign, Rest> - : Assign> - > +> = T extends ObjectCurly + ? IsAny extends true + ? Simplify & T> + : IsNever extends true + ? T + : U extends ObjectCurly + ? Assigner + : U extends readonly ObjectCurly[] + ? U extends readonly [infer H, ...infer R extends ObjectCurly[]] + ? Assign, R> + : Assigner + : never + : never export type { Assign as default } diff --git a/src/types/at.ts b/src/types/at.ts index 13e5748f..010c23aa 100644 --- a/src/types/at.ts +++ b/src/types/at.ts @@ -5,71 +5,225 @@ import type EmptyArray from './empty-array' import type EmptyString from './empty-string' +import type Fallback from './fallback' +import type IfAny from './if-any' +import type IfAnyOrNever from './if-any-or-never' +import type IfKeys from './if-keys' +import type IfNever from './if-never' import type IfTuple from './if-tuple' -import type NumberString from './number-string' +import type Indices from './indices' +import type Integer from './integer' +import type Intersection from './intersection' +import type IsNever from './is-never' +import type Join from './join' +import type Length from './length' +import type Nilable from './nilable' +import type NumberLike from './number-like' import type Numeric from './numeric' +import type NegativeNumeric from './numeric-negative' +import type Opaque from './opaque' +import type Optional from './optional' +import type Primitive from './primitive' +import type PropertyKey from './property-key' +import type NaturalRange from './range-natural' +import type Reverse from './reverse' import type Split from './split' -import type TupleLength from './tuple-length' +import type Stringify from './stringify' +import type UnwrapNumeric from './unwrap-numeric' /** - * Constructs a union of numbers in the range `[0,Max)`. + * Returns a boolean indicating if `K` can be used to index `T` (i.e. `T[K]`). + * + * Numeric {@linkcode Indices} are excluded from `keyof T` when `T` is a tuple. * * @internal * - * @template Max - Upper bound of range (exclusive) - * @template Acc - Accumulator + * @template T - Type to evaluate + * @template K - Keys to evaluate */ -type Enumerate< - Max extends number, - Acc extends readonly number[] = EmptyArray -> = Acc['length'] extends Max - ? Acc[number] - : Enumerate +type Has< + T extends Nilable, + K extends NumberLike +> = IfNever< + T, + false, + IfNever< + K, + false, + T extends string | readonly unknown[] + ? K extends PropertyKey + ? IfKeys< + { + [H in keyof (T extends Primitive ? Opaque : T) as Intersection< + number extends H + ? never + : IfTuple< + T, + H extends Stringify> ? never : H, + H + > extends infer Q extends PropertyKey + ? IfNever< + Q, + Q, + Q extends Numeric + ? Q | UnwrapNumeric + : Q extends number + ? Q | Stringify + : Q + > + : never, + K + >]: H + }, + true, + false + > + : false + : false + > +> /** - * Constructs a union of numerics in the range `[-1 * Max, Max)`. + * Indexes array `T` at `K`. + * + * Supports negative indices. + * + * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/at * * @internal * - * @template Max - Upper bound of range (exclusive) + * @template T - Type to evaluate + * @template K - Index + * @template F - Fallback value type */ -type Range = - | Exclude<`-${Exclude, Enumerate<0>>}`, `-${0}`> - | `-${Max}` - | `${Exclude, Enumerate<0>>}` +type Index< + T extends readonly unknown[], + K extends NumberLike, + F +> = T extends Readonly + ? F + : IfAny< + K, + Fallback ? Optional : T[number], F>, + IfNever< + K, + F, + K extends NumberLike + ? Stringify extends infer X extends Exclude + ? Length extends infer L extends number + ? IfNever< + L, + F, + IsNever< + Intersection< + NegativeNumeric | Numeric | Stringify, + X + > + > extends false + ? Fallback : T[number], F> + : number extends L + ? Fallback, F> + : NaturalRange extends infer R extends number + ? Exclude< + Join<['-', L | R], EmptyString> | Stringify, + '-0' + > extends infer I + ? X extends I + ? 1 extends Length> + ? Fallback + : X extends `-${infer N extends number}` + ? Reverse extends infer B extends readonly T[number][] + ? Fallback<[any, ...B][N], F> + : never + : X extends `${infer N extends number}` + ? Fallback + : never + : F + : never + : never + > + : never + : F + : F + > + > + +/** + * Converts a string to an array. + * + * @see {@linkcode Split} + * + * @internal + * + * @template T - String to convert + */ +type Splitter = Split /** * Indexes `T` at `K`. * - * Partially supports negative indices. + * Supports negative indices. * * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/at * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/at * + * @example + * type X = At<['a', 'b'], 0> + * // 'a' + * @example + * type X = At<['a', 'b'], 2> + * // undefined + * @example + * type X = At<['a', 'b'?], 1, null> + * // 'b' | null + * @example + * type X = At<['a', 'b', 'c'], -1> + * // 'c' + * @example + * type X = At<['a', 'b', 'c'], -4, null> + * // null + * @example + * type X = At<'abc', 2> + * // 'c' + * @example + * type X = At<'abc', -2> + * // 'b' + * @example + * type X = At<'abc', -4, null> + * // null + * @example + * type X = At + * // string | undefined + * @example + * type X = At + * // string | undefined + * * @template T - Type to evaluate * @template K - Index - * @template F - Fallback value + * @template F - Fallback value type */ type At< - T extends string | readonly unknown[], - K extends NumberString, + T extends Nilable, + K extends NumberLike, F = undefined -> = NonNullable extends EmptyArray | EmptyString - ? F - : K extends Numeric | number - ? NonNullable extends string - ? [string] extends [NonNullable] - ? F | T[number] - : Split, ''> extends infer B - ? `${K}` extends Range> - ? B[K & keyof B] - : F - : F - : IfTuple< - NonNullable, - `${K}` extends Range>> ? T[K & keyof T] : F, - F | T[number] - > - : F +> = IfAnyOrNever< + T, + F, + IfNever< + K, + F, + T extends unknown + ? K extends NumberLike + ? Has extends true + ? Fallback & keyof T], F> + : T extends string + ? Index, K, F> + : T extends readonly unknown[] + ? Index + : never + : never + : never + > +> export type { At as default } diff --git a/src/types/booleanish.ts b/src/types/booleanish.ts index 1001d8a3..6a375090 100644 --- a/src/types/booleanish.ts +++ b/src/types/booleanish.ts @@ -8,6 +8,6 @@ import type Stringify from './stringify' /** * Constructs a union of types that represent `true` or `false`. */ -type Booleanish = Stringify | boolean +type Booleanish = Stringify | boolean | 0 | 1 export type { Booleanish as default } diff --git a/src/types/built-in.ts b/src/types/built-in.ts index 5d4bf10a..1a0fea40 100644 --- a/src/types/built-in.ts +++ b/src/types/built-in.ts @@ -14,7 +14,7 @@ import type TypedArray from './typed-array' */ type BuiltIn = | AggregateError - | Array + | any[] | ArrayBuffer | AsyncGenerator | AsyncGeneratorFunction @@ -26,6 +26,7 @@ type BuiltIn = | EvalError | FinalizationRegistry | Fn + | Function | Generator | GeneratorFunction | Intl.Collator @@ -44,6 +45,7 @@ type BuiltIn = | Primitive | Promise | RangeError + | Readonly | ReferenceError | RegExp | Set @@ -55,6 +57,7 @@ type BuiltIn = | WeakMap | WeakRef | WeakSet + | readonly any[] | typeof Intl | typeof Proxy | typeof Reflect diff --git a/src/types/class-abstract.ts b/src/types/class-abstract.ts index 3eb1feaa..1aed99cb 100644 --- a/src/types/class-abstract.ts +++ b/src/types/class-abstract.ts @@ -10,8 +10,8 @@ import type AbstractConstructor from './constructor-abstract' * * [1]: https://www.typescriptlang.org/docs/handbook/2/classes#abstract-classes-and-members * - * @template T - Class prototype - * @template A - Class constructor arguments + * @template T - Class instance type + * @template A - Constructor arguments */ type AbstractClass = AbstractConstructor< T, diff --git a/src/types/class.ts b/src/types/class.ts index 8e59f0b9..0ae5ebf3 100644 --- a/src/types/class.ts +++ b/src/types/class.ts @@ -10,8 +10,8 @@ import type Constructor from './constructor' * * [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes * - * @template T - Class prototype - * @template A - Class constructor arguments + * @template T - Class instance type + * @template A - Constructor arguments */ type Class = Constructor & { prototype: T diff --git a/src/types/constructor-abstract.ts b/src/types/constructor-abstract.ts index eaf3227e..659587ad 100644 --- a/src/types/constructor-abstract.ts +++ b/src/types/constructor-abstract.ts @@ -8,7 +8,7 @@ * * [1]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-2.html#abstract-construct-signatures * - * @template T - Class prototype + * @template T - Constructor instance type * @template A - Constructor arguments */ type AbstractConstructor = abstract new ( diff --git a/src/types/constructor.ts b/src/types/constructor.ts index a798eb40..f269573f 100644 --- a/src/types/constructor.ts +++ b/src/types/constructor.ts @@ -8,7 +8,7 @@ * * [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes/constructor * - * @template T - Class prototype + * @template T - Constructor instance type * @template A - Constructor arguments */ type Constructor = new (...args: A) => T diff --git a/src/types/dot.ts b/src/types/dot.ts new file mode 100644 index 00000000..07805b38 --- /dev/null +++ b/src/types/dot.ts @@ -0,0 +1,11 @@ +/** + * @file Type Definitions - Dot + * @module tutils/types/Dot + */ + +/** + * A dot character (`'.'`). + */ +type Dot = '.' + +export type { Dot as default } diff --git a/src/types/empty-array.ts b/src/types/empty-array.ts index be332c9d..40fd132b 100644 --- a/src/types/empty-array.ts +++ b/src/types/empty-array.ts @@ -6,6 +6,6 @@ /** * An empty array. */ -type EmptyArray = Readonly<[]> | [] +type EmptyArray = [] export type { EmptyArray as default } diff --git a/src/types/empty-object.ts b/src/types/empty-object.ts index 7f32fef6..d5b00992 100644 --- a/src/types/empty-object.ts +++ b/src/types/empty-object.ts @@ -3,11 +3,18 @@ * @module tutils/types/EmptyObject */ -import type PropertyKey from './property-key' +/** + * Unique {@linkcode EmptyObject} symbol. + * + * @internal + * + * @const {symbol} tag + */ +export declare const tag: unique symbol /** * An empty object. */ -type EmptyObject = Record +type EmptyObject = { [tag]?: never } export type { EmptyObject as default } diff --git a/src/types/ensure-string.ts b/src/types/ensure-string.ts deleted file mode 100644 index 8b969161..00000000 --- a/src/types/ensure-string.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @file Type Definitions - EnsureString - * @module tutils/types/EnsureString - */ - -/** - * Ensures type `T` intersects with type `string`. - * - * @see https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#intersection-types - * - * @template T - Type to combine with `string` - */ -type EnsureString = T & string - -export type { EnsureString as default } diff --git a/src/types/exact-optional-property-types.ts b/src/types/exact-optional-property-types.ts index bb7d1645..cafd4b0c 100644 --- a/src/types/exact-optional-property-types.ts +++ b/src/types/exact-optional-property-types.ts @@ -3,6 +3,7 @@ * @module tutils/types/ExactOptionalPropertyTypes */ +import type IfNever from './if-never' import type ObjectCurly from './object-curly' import type Simplify from './simplify' @@ -13,12 +14,14 @@ import type Simplify from './simplify' * * [1]: https://github.com/Microsoft/TypeScript/issues/46969 * + * @todo examples + * * @template T - Type to evaluate */ type ExactOptionalPropertyTypes = Simplify<{ - [K in keyof T]: Exclude extends never - ? T[K] - : Exclude + [K in keyof T]: Exclude extends infer V + ? IfNever + : never }> export type { ExactOptionalPropertyTypes as default } diff --git a/src/types/fallback.ts b/src/types/fallback.ts index 77425506..b0a8f296 100644 --- a/src/types/fallback.ts +++ b/src/types/fallback.ts @@ -3,20 +3,28 @@ * @module tutils/types/Fallback */ -import type NIL from './nil' - /** * Fallback value helper. * - * Resolves to `Exclude | F` if `T` extends `Condition`, or `T` - * otherwise. + * Resolves to `Exclude | F` if `T` extends `C`, or `T` otherwise. + * + * @example + * type X = Fallback + * // number | string + * @example + * type X = Fallback, number, NIL> + * // number | string + * @example + * type X = Fallback + * // number + * @example + * type X = Fallback + * // number * - * @template T - Value to evaluate + * @template T - Type to evaluate * @template F - Fallback value type - * @template Condition - Fallback condition + * @template C - Fallback condition type */ -type Fallback = T extends Condition - ? Exclude | F - : T +type Fallback = T extends C ? Exclude | F : T export type { Fallback as default } diff --git a/src/types/get.ts b/src/types/get.ts index 5937ed71..4141329a 100644 --- a/src/types/get.ts +++ b/src/types/get.ts @@ -4,14 +4,14 @@ */ import type At from './at' -import type EmptyArray from './empty-array' -import type EmptyObject from './empty-object' -import type EmptyString from './empty-string' +import type Dot from './dot' import type Fallback from './fallback' -import type IfEqual from './if-equal' -import type IfUndefined from './if-undefined' -import type NumberString from './number-string' -import type Numeric from './numeric' +import type IfAny from './if-any' +import type IfKeys from './if-keys' +import type IfNever from './if-never' +import type Join from './join' +import type Keyof from './keyof' +import type NumberLike from './number-like' import type PropertyKey from './property-key' /** @@ -19,45 +19,77 @@ import type PropertyKey from './property-key' * * Supports dot-notation for nested object paths and array-indexing. * + * @todo examples + * * @template T - Type to evaluate - * @template K - Property paths - * @template F - Fallback value + * @template K - Property to select + * @template F - Fallback value type */ -type Get = T extends - | EmptyArray - | EmptyObject - | EmptyString - ? F - : K extends Exclude, number> - ? Fallback[K], F, undefined> - : K extends `${infer H extends NumberString}.${infer Tail}` - ? NonNullable extends string | readonly unknown[] - ? H extends Numeric | number - ? At, H> extends infer U - ? IfUndefined< - U, - F, - IfEqual< - U, - NonNullable, - Get[H & keyof NonNullable], Tail, F>, - F | Get[H & keyof NonNullable], Tail, F> - > - > - : never - : never - : H extends keyof NonNullable - ? IfEqual< - NonNullable[H], - NonNullable[H]>, - Get[H & keyof NonNullable], Tail, F>, - F | Get[H & keyof NonNullable], Tail, F> - > - : Get[H & keyof NonNullable], Tail, F> - : NonNullable extends string | readonly unknown[] - ? K extends Numeric | number - ? At, K, F> - : F - : F +type Get = IfAny< + T, + T, + IfNever< + K, + F, + T extends unknown + ? IfKeys< + T, + Keyof extends infer Q extends keyof T + ? IfAny extends infer K extends PropertyKey + ? K extends Join< + [infer H extends string, infer R extends string], + Dot + > + ? H extends NumberLike | Q + ? Fallback< + Get< + T extends string | readonly unknown[] + ? H extends NumberLike + ? At + : H extends keyof T + ? T[H] + : never + : T[H extends `${infer N extends Extract< + keyof T, + number + >}` + ? N + : H & keyof T], + R, + F + >, + F + > + : F + : K extends NumberLike | Q + ? T extends string | readonly unknown[] + ? IfNever< + K, + F, + K extends NumberLike + ? At + : Fallback + > + : IfNever< + K, + F, + Fallback< + T[K extends `${infer N extends Extract< + keyof T, + number + >}` + ? N + : K & keyof T], + F + > + > + : F + : never + : never, + F + > + : F + > +> export type { Get as default } diff --git a/src/types/has-keys.ts b/src/types/has-keys.ts new file mode 100644 index 00000000..cc841065 --- /dev/null +++ b/src/types/has-keys.ts @@ -0,0 +1,45 @@ +/** + * @file Type Definitions - HasKeys + * @module tutils/types/HasKeys + */ + +import type IfNever from './if-never' +import type Keyof from './keyof' + +/** + * Returns a boolean indicating if `T` has any keys. + * + * @example + * type X = HasKeys<{ hello: string; world: string }> + * // true + * @example + * type X = HasKeys + * // true + * @example + * type X = HasKeys + * // true + * @example + * type X = HasKeys<{}> + * // false + * @example + * type X = HasKeys + * // false + * @example + * type X = HasKeys + * // true + * @example + * type X = HasKeys + * // false + * @example + * type X = HasKeys + * // false + * + * @template T - Type to evaluate + */ +type HasKeys = IfNever< + T, + false, + T extends unknown ? IfNever, false, true> : false +> + +export type { HasKeys as default } diff --git a/src/types/head.ts b/src/types/head.ts index 9c46607a..5c3d0982 100644 --- a/src/types/head.ts +++ b/src/types/head.ts @@ -3,9 +3,11 @@ * @module tutils/types/Head */ -import type IfArray from './if-array' -import type IfString from './if-string' -import type NumberString from './number-string' +import type EmptyArray from './empty-array' +import type EmptyString from './empty-string' +import type IfAny from './if-any' +import type Join from './join' +import type Optional from './optional' /** * Returns the head of `T`. @@ -14,22 +16,32 @@ import type NumberString from './number-string' * * - `never` if `T` is not an array or string * - first item in `T` if `T` is an array - * - first segment before the first delimiter if `T` is a string + * - first segment in `T` if `T` is a string + * + * @todo examples * * @template T - Type to evaluate * @template D - String delimiter */ -type Head = T extends string | readonly unknown[] - ? IfString< - T, - T extends `${infer First}${D}${NumberString}` ? First : T, - IfArray< - T, - unknown, - T extends [infer First, ...infer Rest] ? First : T[0], - T - > - > - : never +type Head< + T extends string | readonly unknown[], + D extends string = EmptyString +> = IfAny< + T, + never, + T extends string + ? T extends EmptyString + ? never + : T extends Join<[infer H extends string, D, string], EmptyString> + ? H + : T + : T extends Readonly + ? never + : T extends readonly [infer H extends T[0], ...T[number][]] + ? H + : T extends readonly [(infer H extends T[0])?, ...T[number][]] + ? Optional + : never +> export type { Head as default } diff --git a/src/types/if-any-or-never.ts b/src/types/if-any-or-never.ts index cc85be27..2403313b 100644 --- a/src/types/if-any-or-never.ts +++ b/src/types/if-any-or-never.ts @@ -6,14 +6,27 @@ import type IsAnyOrNever from './is-any-or-never' /** - * Returns a type that indicates if `T` is `any` or `never`. + * Returns a type that indicates if `U` is `any` or `never`. * * @see {@linkcode IsAnyOrNever} * - * @template T - Type to evaluate - * @template True - Type if `T` is `any` or `never` - * @template False - Type if `T` is not `any` or `never` + * @example + * type X = IfAnyOrNever + * // 1 + * @example + * type X = IfAnyOrNever + * // 1 + * @example + * type X = IfAnyOrNever + * // 0 + * @example + * type X = IfAnyOrNever<{ hello: string; world: string }, 1, 0> + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` is `any` or `never` + * @template F - Type if `U` is not `any` or `never` */ -type IfAnyOrNever = IsAnyOrNever extends true ? True : False +type IfAnyOrNever = IsAnyOrNever extends true ? T : F export type { IfAnyOrNever as default } diff --git a/src/types/if-any.ts b/src/types/if-any.ts index 3c321ad2..fae5937b 100644 --- a/src/types/if-any.ts +++ b/src/types/if-any.ts @@ -6,14 +6,27 @@ import type IsAny from './is-any' /** - * Returns a type that indicates if `T` is `any`. + * Returns a type that indicates if `U` is `any`. * * @see {@linkcode IsAny} * - * @template T - Type to evaluate - * @template True - Type if `T` is `any` - * @template False - Type if `T` is not `any` + * @example + * type X = IfAny + * // 1 + * @example + * type X = IfAny + * // 0 + * @example + * type X = IfAny + * // 0 + * @example + * type X = IfAny<{ hello: string; world: string }, 1, 0> + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` is `any` + * @template F - Type if `U` is not `any` */ -type IfAny = IsAny extends true ? True : False +type IfAny = IsAny extends true ? T : F export type { IfAny as default } diff --git a/src/types/if-array.ts b/src/types/if-array.ts index 30366a35..ce00217c 100644 --- a/src/types/if-array.ts +++ b/src/types/if-array.ts @@ -3,18 +3,51 @@ * @module tutils/types/IfArray */ +import type IfNever from './if-never' import type IsArray from './is-array' /** - * Returns a type that indicates if `T` is an array of type `V`. + * Returns a type that indicates if `U` extends an array of type `V`. * * @see {@linkcode IsArray} * - * @template T - Type to evaluate + * @example + * type X = IfArray + * // 1 + * @example + * type X = IfArray + * // 1 + * @example + * type X = IfArray + * // 0 + * @example + * type X = IfArray + * // 1 + * @example + * type X = IfArray + * // 1 + * @example + * type X = IfArray + * // 0 + * @example + * type X = IfArray + * // 0 + * @example + * type X = IfArray + * // 0 + * @example + * type X = IfArray + * // 0 + * + * @template U - Type to evaluate * @template V - Array element type - * @template True - Type if `T` is array of type `V` - * @template False - Type if `T` is not array of type `V` + * @template T - Type if `U` extends array of type `V` + * @template F - Type if `U` does not extends array of type `V` */ -type IfArray = IsArray extends true ? True : False +type IfArray = IfNever< + U, + F, + U extends unknown ? (IsArray extends true ? T : F) : F +> export type { IfArray as default } diff --git a/src/types/if-big-int.ts b/src/types/if-big-int.ts index af8219ba..5174f938 100644 --- a/src/types/if-big-int.ts +++ b/src/types/if-big-int.ts @@ -3,17 +3,44 @@ * @module tutils/types/IfBigInt */ +import type IfNever from './if-never' import type IsBigInt from './is-big-int' /** - * Returns a type that indicates if `T` is a `bigint`. + * Returns a type that indicates if `U` extends `bigint`. * * @see {@linkcode IsBigInt} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `bigint` - * @template False - Type if `T` is not a `bigint` + * @example + * type X = IfBigInt<3n, 1, 0> + * // 1 + * @example + * type X = IfBigInt + * // 1 + * @example + * type X = IfBigInt<3, 1, 0> + * // 0 + * @example + * type X = IfBigInt + * // 0 + * @example + * type X = IfBigInt + * // 0 + * @example + * type X = IfBigInt + * // 0 + * @example + * type X = IfBigInt + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `bigint` + * @template F - Type if `U` does not extend `bigint` */ -type IfBigInt = IsBigInt extends true ? True : False +type IfBigInt = IfNever< + U, + F, + U extends unknown ? (IsBigInt extends true ? T : F) : F +> export type { IfBigInt as default } diff --git a/src/types/if-boolean.ts b/src/types/if-boolean.ts index e1ca09d5..584b80da 100644 --- a/src/types/if-boolean.ts +++ b/src/types/if-boolean.ts @@ -3,17 +3,47 @@ * @module tutils/types/IfBoolean */ +import type IfNever from './if-never' import type IsBoolean from './is-boolean' /** - * Returns a type that indicates if `T` is a `boolean`. + * Returns a type that indicates if `U` extends `boolean`. * * @see {@linkcode IsBoolean} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `boolean` - * @template False - Type if `T` is not a `boolean` + * @example + * type X = IfBoolean + * // 1 + * @example + * type X = IfBoolean + * // 1 + * @example + * type X = IfBoolean + * // 1 + * @example + * type X = IfBoolean + * // 0 | 1 + * @example + * type X = IfBoolean<1, 1, 0> + * // 0 + * @example + * type X = IfBoolean + * // 0 + * @example + * type X = IfBoolean + * // 0 + * @example + * type X = IfBoolean + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `boolean` + * @template F - Type if `U` does not extend `boolean` */ -type IfBoolean = IsBoolean extends true ? True : False +type IfBoolean = IfNever< + U, + F, + U extends unknown ? (IsBoolean extends true ? T : F) : F +> export type { IfBoolean as default } diff --git a/src/types/if-equal.ts b/src/types/if-equal.ts index 64d45b6e..ea4fe83d 100644 --- a/src/types/if-equal.ts +++ b/src/types/if-equal.ts @@ -10,11 +10,21 @@ import type IsEqual from './is-equal' * * @see {@linkcode IsEqual} * + * @example + * type X = IfEqual<{ hello: 'world' }, { hello: 'world' }, 1, 0> + * // 1 + * @example + * type X = IfEqual<{ hello: 'world' }, { hello?: 'world' }, 1, 0> + * // 0 + * @example + * type X = IfEqual<{ hello: 'world' }, { readonly hello: 'world' }, 1, 0> + * // 0 + * * @template A - First type to evaluate * @template B - Second type to evaluate - * @template True - Type if `A` and `B` are equal - * @template False - Type if `A` and `B` are not equal + * @template T - Type if `A` and `B` are equal + * @template F - Type if `A` and `B` are not equal */ -type IfEqual = IsEqual extends true ? True : False +type IfEqual = IsEqual extends true ? T : F export type { IfEqual as default } diff --git a/src/types/if-false.ts b/src/types/if-false.ts new file mode 100644 index 00000000..00d5cabf --- /dev/null +++ b/src/types/if-false.ts @@ -0,0 +1,46 @@ +/** + * @file Type Definitions - IfFalse + * @module tutils/types/IfFalse + */ + +import type IfNever from './if-never' +import type IsFalse from './is-false' + +/** + * Returns a type that indicates if `U` extends `false`. + * + * @see {@linkcode IsFalse} + * + * @example + * type X = IfFalse + * // 1 + * @example + * type X = IfFalse + * // 0 | 1 + * @example + * type X = IfFalse + * // 0 + * @example + * type X = IfFalse<0, 1, 0> + * // 0 + * @example + * type X = IfFalse + * // 0 + * @example + * type X = IfFalse + * // 0 + * @example + * type X = IfFalse + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `false` + * @template F - Type if `U` does not extend `false` + */ +type IfFalse = IfNever< + U, + F, + U extends unknown ? (IsFalse extends true ? T : F) : F +> + +export type { IfFalse as default } diff --git a/src/types/if-function.ts b/src/types/if-function.ts index f0f2a8d6..62dc9d33 100644 --- a/src/types/if-function.ts +++ b/src/types/if-function.ts @@ -3,17 +3,47 @@ * @module tutils/types/IfFunction */ +import type IfNever from './if-never' import type IsFunction from './is-function' /** - * Returns a type that indicates if `T` is a function. + * Returns a type that indicates if `U` extends `Fn`. * * @see {@linkcode IsFunction} * - * @template T - Type to evaluate - * @template True - Type if `T` is a function - * @template False - Type if `T` is not a function + * @example + * type X = IfFunction<(n: number) => string, 1, 0> + * // 1 + * @example + * type X = IfFunction + * // 1 + * @example + * type X = IfFunction, 1, 0> + * // 1 + * @example + * type X = IfFunction string>, 1, 0> + * // 0 | 1 + * @example + * type X = IfFunction + * // 0 + * @example + * type X = IfFunction + * // 0 + * @example + * type X = IfFunction + * // 0 + * @example + * type X = IfFunction + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `Function` + * @template F - Type if `U` does not extend `Function` */ -type IfFunction = IsFunction extends true ? True : False +type IfFunction = IfNever< + U, + F, + U extends unknown ? (IsFunction extends true ? T : F) : F +> export type { IfFunction as default } diff --git a/src/types/if-index-signature.ts b/src/types/if-index-signature.ts new file mode 100644 index 00000000..f8151cfd --- /dev/null +++ b/src/types/if-index-signature.ts @@ -0,0 +1,38 @@ +/** + * @file Type Definitions - IfIndexSignature + * @module tutils/types/IfIndexSignature + */ + +import type IfNever from './if-never' +import type IsIndexSignature from './is-index-signature' +import type PropertyKey from './property-key' + +/** + * Returns a type that indicates if `K` is an index signature of `U`. + * + * @see {@linkcode IsIndexSignature} + * + * @todo examples + * + * @template U - Type to evaluate + * @template K - Key to evaluate + * @template T - Type if `K` is index signature + * @template F - Type if `K` is not index signature + */ +type IfIndexSignature = IfNever< + U, + F, + IfNever< + K, + F, + U extends unknown + ? K extends unknown + ? IsIndexSignature extends true + ? T + : F + : F + : F + > +> + +export type { IfIndexSignature as default } diff --git a/src/types/if-integer-negative.ts b/src/types/if-integer-negative.ts new file mode 100644 index 00000000..28755dab --- /dev/null +++ b/src/types/if-integer-negative.ts @@ -0,0 +1,49 @@ +/** + * @file Type Definitions - IfNegativeInteger + * @module tutils/types/IfNegativeInteger + */ + +import type IfNever from './if-never' +import type IsNegativeInteger from './is-integer-negative' + +/** + * Returns a type that indicates if `U` extends a negative integer. + * + * @see {@linkcode IsNegativeInteger} + * + * @example + * type X = IfNegativeInteger<-1, 1, 0> + * // 1 + * @example + * type X = IfNegativeInteger + * // 0 | 1 + * @example + * type X = IfNegativeInteger<0, 1, 0> + * // 0 + * @example + * type X = IfNegativeInteger<1, 1, 0> + * // 0 + * @example + * type X = IfNegativeInteger + * // 0 + * @example + * type X = IfNegativeInteger + * // 0 + * @example + * type X = IfNegativeInteger + * // 0 + * @example + * type X = IfNegativeInteger + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends a negative integer + * @template F - Type if `U` does not extend a negative integer + */ +type IfNegativeInteger = IfNever< + U, + F, + U extends unknown ? (IsNegativeInteger extends true ? T : F) : F +> + +export type { IfNegativeInteger as default } diff --git a/src/types/if-integer.ts b/src/types/if-integer.ts new file mode 100644 index 00000000..63f3ba34 --- /dev/null +++ b/src/types/if-integer.ts @@ -0,0 +1,49 @@ +/** + * @file Type Definitions - IfInteger + * @module tutils/types/IfInteger + */ + +import type IfNever from './if-never' +import type IsInteger from './is-integer' + +/** + * Returns a type that indicates if `U` extends an integer. + * + * @see {@linkcode IsInteger} + * + * @example + * type X = IfInteger<-1, 1, 0> + * // 1 + * @example + * type X = IfInteger<0, 1, 0> + * // 1 + * @example + * type X = IfInteger<1, 1, 0> + * // 1 + * @example + * type X = IfInteger + * // 0 | 1 + * @example + * type X = IfInteger + * // 0 + * @example + * type X = IfInteger + * // 0 + * @example + * type X = IfInteger + * // 0 + * @example + * type X = IfInteger + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends an integer + * @template False - Type if `U` does not extend an integer + */ +type IfInteger = IfNever< + U, + F, + U extends unknown ? (IsInteger extends true ? T : F) : F +> + +export type { IfInteger as default } diff --git a/src/types/if-json-primitive.ts b/src/types/if-json-primitive.ts index 98ba6b73..6332b3d3 100644 --- a/src/types/if-json-primitive.ts +++ b/src/types/if-json-primitive.ts @@ -3,19 +3,50 @@ * @module tutils/types/IfJsonPrimitive */ +import type IfNever from './if-never' import type IsJsonPrimitive from './is-json-primitive' /** - * Returns a type that indicates if `T` is a `JsonPrimitive`. + * Returns a type that indicates if `U` extends `JsonPrimitive`. * * @see {@linkcode IsJsonPrimitive} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `JsonPrimitive` - * @template False - Type if `T` is not a `JsonPrimitive` + * @example + * type X = IfJsonPrimitive + * // 1 + * @example + * type X = IfJsonPrimitive + * // 1 + * @example + * type X = IfJsonPrimitive + * // 1 + * @example + * type X = IfJsonPrimitive + * // 1 + * @example + * type X = IfJsonPrimitive + * // 0 | 1 + * @example + * type X = IfJsonPrimitive<{ hello: string; world: string }, 1, 0> + * // 0 + * @example + * type X = IfJsonPrimitive + * // 0 + * @example + * type X = IfJsonPrimitive + * // 0 + * @example + * type X = IfJsonPrimitive + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `JsonPrimitive` + * @template F - Type if `U` does not extend `JsonPrimitive` */ -type IfJsonPrimitive = IsJsonPrimitive extends true - ? True - : False +type IfJsonPrimitive = IfNever< + U, + F, + U extends unknown ? (IsJsonPrimitive extends true ? T : F) : F +> export type { IfJsonPrimitive as default } diff --git a/src/types/if-key-optional-exact.ts b/src/types/if-key-optional-exact.ts index 05aff433..05b45fe5 100644 --- a/src/types/if-key-optional-exact.ts +++ b/src/types/if-key-optional-exact.ts @@ -3,24 +3,37 @@ * @module tutils/types/IfExactOptionalKey */ +import type IfNever from './if-never' import type IsExactOptionalKey from './is-key-optional-exact' import type PropertyKey from './property-key' /** - * Returns a type that indicates if `K` is an exact optional property of `T`. + * Returns a type that indicates if `K` extends an exact optional property of + * `U`. * * @see {@linkcode IsExactOptionalKey} * - * @template T - Type to evaluate + * @todo examples + * + * @template U - Type to evaluate * @template K - Key to evaluate - * @template True - Type if `K` is exact optional property - * @template False - Type if `K` is not exact optional property + * @template T - Type if `K` extends exact optional property + * @template F - Type if `K` does not extend exact optional property */ -type IfExactOptionalKey< - T, - K extends PropertyKey, - True, - False -> = IsExactOptionalKey extends true ? True : False +type IfExactOptionalKey = IfNever< + U, + F, + IfNever< + K, + F, + U extends unknown + ? K extends unknown + ? IsExactOptionalKey extends true + ? T + : F + : F + : F + > +> export type { IfExactOptionalKey as default } diff --git a/src/types/if-key-optional.ts b/src/types/if-key-optional.ts index f76f2536..96747a5c 100644 --- a/src/types/if-key-optional.ts +++ b/src/types/if-key-optional.ts @@ -3,24 +3,36 @@ * @module tutils/types/IfOptionalKey */ +import type IfNever from './if-never' import type IsOptionalKey from './is-key-optional' import type PropertyKey from './property-key' /** - * Returns a type that indicates if `K` is an optional property of `T`. + * Returns a type that indicates if `K` extends an optional property of `U`. * * @see {@linkcode IsOptionalKey} * - * @template T - Type to evaluate + * @todo examples + * + * @template U - Type to evaluate * @template K - Key to evaluate - * @template True - Type if `K` is optional property - * @template False - Type if `K` is not optional property + * @template T - Type if `K` extends optional property + * @template F - Type if `K` does not extend optional property */ -type IfOptionalKey = IsOptionalKey< - T, - K -> extends true - ? True - : False +type IfOptionalKey = IfNever< + U, + F, + IfNever< + K, + F, + U extends unknown + ? K extends unknown + ? IsOptionalKey extends true + ? T + : F + : F + : F + > +> export type { IfOptionalKey as default } diff --git a/src/types/if-key-readonly.ts b/src/types/if-key-readonly.ts new file mode 100644 index 00000000..0910cf76 --- /dev/null +++ b/src/types/if-key-readonly.ts @@ -0,0 +1,38 @@ +/** + * @file Type Definitions - IfReadonlyKey + * @module tutils/types/IfReadonlyKey + */ + +import type IfNever from './if-never' +import type IsReadonlyKey from './is-key-readonly' +import type PropertyKey from './property-key' + +/** + * Returns a type that indicates if `K` extends a readonly property of `U`. + * + * @see {@linkcode IsReadonlyKey} + * + * @todo examples + * + * @template U - Type to evaluate + * @template K - Key to evaluate + * @template T - Type if `K` extends readonly property + * @template F - Type if `K` does not extend readonly property + */ +type IfReadonlyKey = IfNever< + U, + F, + IfNever< + K, + F, + U extends unknown + ? K extends unknown + ? IsReadonlyKey extends true + ? T + : F + : F + : F + > +> + +export type { IfReadonlyKey as default } diff --git a/src/types/if-key-required.ts b/src/types/if-key-required.ts index 9c21f0c8..06941937 100644 --- a/src/types/if-key-required.ts +++ b/src/types/if-key-required.ts @@ -3,24 +3,36 @@ * @module tutils/types/IfRequiredKey */ +import type IfNever from './if-never' import type IsRequiredKey from './is-key-required' import type PropertyKey from './property-key' /** - * Returns a type that indicates if `K` is a required property of `T`. + * Returns a type that indicates if `K` extends a required property of `U`. * * @see {@linkcode IsRequiredKey} * - * @template T - Type to evaluate + * @todo examples + * + * @template U - 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 + * @template T - Type if `K` extends required property + * @template F - Type if `K` does not extend required property */ -type IfRequiredKey = IsRequiredKey< - T, - K -> extends true - ? True - : False +type IfRequiredKey = IfNever< + U, + F, + IfNever< + K, + F, + U extends unknown + ? K extends unknown + ? IsRequiredKey extends true + ? T + : F + : F + : F + > +> export type { IfRequiredKey as default } diff --git a/src/types/if-keys.ts b/src/types/if-keys.ts new file mode 100644 index 00000000..f77ae58a --- /dev/null +++ b/src/types/if-keys.ts @@ -0,0 +1,49 @@ +/** + * @file Type Definitions - IfKeys + * @module tutils/types/IfKeys + */ + +import type HasKeys from './has-keys' +import type IfNever from './if-never' + +/** + * Returns a type that indicates if `U` has any keys. + * + * @see {@linkcode HasKeys} + * + * @example + * type X = IfKeys<{ hello: string; world: string }, 1, 0> + * // 1 + * @example + * type X = IfKeys + * // 1 + * @example + * type X = IfKeys + * // 1 + * @example + * type X = IfKeys<{}, 1, 0> + * // 0 + * @example + * type X = IfKeys + * // 0 + * @example + * type X = IfKeys + * // 1 + * @example + * type X = IfKeys + * // 0 + * @example + * type X = IfKeys + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` has keys + * @template F - Type if `U` does not have any keys + */ +type IfKeys = IfNever< + U, + F, + U extends unknown ? (HasKeys extends true ? T : F) : F +> + +export type { IfKeys as default } diff --git a/src/types/if-literal.ts b/src/types/if-literal.ts new file mode 100644 index 00000000..d70bcddc --- /dev/null +++ b/src/types/if-literal.ts @@ -0,0 +1,78 @@ +/** + * @file Type Definitions - IfLiteral + * @module tutils/types/IfLiteral + */ + +import type IsLiteral from './is-literal' +import type IsNever from './is-never' +import type Primitive from './primitive' + +/** + * Returns a type that indicates if `U` extends a literal {@linkcode Primitive}. + * + * @see {@linkcode IsLiteral} + * @see https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types + * + * @example + * type X = IfLiteral<3n, Primitive, 1, 0> + * // 1 + * @example + * type X = IfLiteral<3n, bigint, 1, 0> + * // 1 + * @example + * type X = IfLiteral + * // 1 + * @example + * type X = IfLiteral + * // 1 + * @example + * type X = IfLiteral<3, Primitive, 1, 0> + * // 1 + * @example + * type X = IfLiteral<3, number, 1, 0> + * // 1 + * @example + * type X = IfLiteral<'msg', Primitive, 1, 0> + * // 1 + * @example + * type X = IfLiteral<'msg', string, 1, 0> + * // 1 + * @example + * type X = IfLiteral + * // 1 + * @example + * type X = IfLiteral + * // 0 | 1 + * @example + * type X = IfLiteral, Primitive, 1, 0> + * // 0 + * @example + * type X = IfLiteral<3 & { id: string }, Primitive, 1, 0> + * // 0 + * @example + * type X = IfLiteral<{ hello: 'world' }, Primitive, 1, 0> + * // 0 + * @example + * type X = IfLiteral + * // 0 + * @example + * type X = IfLiteral + * // 0 + * @example + * type X = IfLiteral + * // 0 + * + * @template U - Type to evaluate + * @template P - Primitive value type + * @template T - Type if `U` extends a literal `Primitive` + * @template F - Type if `U` does not extend a literal `Primitive` + */ +type IfLiteral = IsNever extends true + ? F + : U extends unknown + ? IsLiteral extends true + ? T + : F + : F + +export type { IfLiteral as default } diff --git a/src/types/if-negative.ts b/src/types/if-negative.ts new file mode 100644 index 00000000..b6b6f773 --- /dev/null +++ b/src/types/if-negative.ts @@ -0,0 +1,64 @@ +/** + * @file Type Definitions - IfNegative + * @module tutils/types/IfNegative + */ + +import type IfNever from './if-never' +import type IsNegative from './is-negative' + +/** + * Returns a type that indicates if `U` extends a negative value. + * + * Negative values include: + * + * - `NegativeNumeric` + * - A negative `bigint` (e.g. `-1n`) + * - A negative `number` (e.g. `-1`) + * + * @see {@linkcode IsNegative} + * + * @example + * type X = IfNegative<-13, 1, 0> + * // 1 + * @example + * type X = IfNegative<-13n, 1, 0> + * // 1 + * @example + * type X = IfNegative<'-13', 1, 0> + * // 1 + * @example + * type X = IfNegative + * // 1 + * @example + * type X = IfNegative + * // 0 | 1 + * @example + * type X = IfNegative + * // 0 + * @example + * type X = IfNegative + * // 0 + * @example + * type X = IfNegative + * // 0 + * @example + * type X = IfNegative + * // 0 + * @example + * type X = IfNegative + * // 0 + * @example + * type X = IfNegative + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends a negative value + * @template F - Type if `U` does not extend a negative value + */ +type IfNegative = IfNever< + U, + F, + U extends unknown ? (IsNegative extends true ? T : F) : F +> + +export type { IfNegative as default } diff --git a/src/types/if-never.ts b/src/types/if-never.ts index dda098dd..aad37e41 100644 --- a/src/types/if-never.ts +++ b/src/types/if-never.ts @@ -6,14 +6,27 @@ import type IsNever from './is-never' /** - * Returns a type that indicates if `T` is `never`. + * Returns a type that indicates if `U` is `never`. * * @see {@linkcode IsNever} * - * @template T - Type to evaluate - * @template True - Type if `T` is `never` - * @template False - Type if `T` is not `never` + * @example + * type X = IfNever + * // 1 + * @example + * type X = IfNever + * // 0 + * @example + * type X = IfNever + * // 0 + * @example + * type X = IfNever<{ hello: string; world: string }, 1, 0> + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` is `never` + * @template F - Type if `U` is not `never` */ -type IfNever = IsNever extends true ? True : False +type IfNever = IsNever extends true ? T : F export type { IfNever as default } diff --git a/src/types/if-nil.ts b/src/types/if-nil.ts index 89fb9ce0..4d272476 100644 --- a/src/types/if-nil.ts +++ b/src/types/if-nil.ts @@ -3,17 +3,47 @@ * @module tutils/types/IfNil */ +import type IfNever from './if-never' import type IsNil from './is-nil' /** - * Returns a type that indicates if `T` is `NIL`. + * Returns a type that indicates if `U` extends `NIL`. * * @see {@linkcode IsNil} * - * @template T - Type to evaluate - * @template True - Type if `T` is `NIL` - * @template False - Type if `T` is not `NIL` + * @example + * type X = IfNil + * // 1 + * @example + * type X = IfNil + * // 1 + * @example + * type X = IfNil + * // 1 + * @example + * type X = IfNil + * // 0 | 1 + * @example + * type X = IfNil + * // 0 + * @example + * type X = IfNil + * // 0 + * @example + * type X = IfNil + * // 0 + * @example + * type X = IfNil + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `NIL` + * @template F - Type if `U` does not extend `NIL` */ -type IfNil = IsNil extends true ? True : False +type IfNil = IfNever< + U, + F, + U extends unknown ? (IsNil extends true ? T : F) : F +> export type { IfNil as default } diff --git a/src/types/if-null.ts b/src/types/if-null.ts index 403ed81c..9dc73879 100644 --- a/src/types/if-null.ts +++ b/src/types/if-null.ts @@ -3,17 +3,41 @@ * @module tutils/types/IfNull */ +import type IfNever from './if-never' import type IsNull from './is-null' /** - * Returns a type that indicates if `T` is `null`. + * Returns a type that indicates if `U` extends `null`. * * @see {@linkcode IsNull} * - * @template T - Type to evaluate - * @template True - Type if `T` is `null` - * @template False - Type if `T` is not `null` + * @example + * type X = IfNull + * // 1 + * @example + * type X = IfNull + * // 0 | 1 + * @example + * type X = IfNull + * // 0 + * @example + * type X = IfNull + * // 0 + * @example + * type X = IfNull + * // 0 + * @example + * type X = IfNull + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `null` + * @template F - Type if `U` does not extend `null` */ -type IfNull = IsNull extends true ? True : False +type IfNull = IfNever< + U, + F, + U extends unknown ? (IsNull extends true ? T : F) : F +> export type { IfNull as default } diff --git a/src/types/if-number.ts b/src/types/if-number.ts index a30e283c..3ab4c462 100644 --- a/src/types/if-number.ts +++ b/src/types/if-number.ts @@ -3,17 +3,44 @@ * @module tutils/types/IfNumber */ +import type IfNever from './if-never' import type IsNumber from './is-number' /** - * Returns a type that indicates if `T` is a `number`. + * Returns a type that indicates if `U` extends `number`. * * @see {@linkcode IsNumber} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `number` - * @template False - Type if `T` is not a `number` + * @example + * type X = IfNumber<3, 1, 0> + * // 1 + * @example + * type X = IfNumber + * // 1 + * @example + * type X = IfNumber<3n, 1, 0> + * // 0 + * @example + * type X = IfNumber + * // 0 + * @example + * type X = IfNumber + * // 0 + * @example + * type X = IfNumber + * // 0 + * @example + * type X = IfNumber + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `number` + * @template F - Type if `U` does not extend `number` */ -type IfNumber = IsNumber extends true ? True : False +type IfNumber = IfNever< + U, + F, + U extends unknown ? (IsNumber extends true ? T : F) : F +> export type { IfNumber as default } diff --git a/src/types/if-numeric-negative.ts b/src/types/if-numeric-negative.ts new file mode 100644 index 00000000..0b573bfc --- /dev/null +++ b/src/types/if-numeric-negative.ts @@ -0,0 +1,55 @@ +/** + * @file Type Definitions - IfNegativeNumeric + * @module tutils/types/IfNegativeNumeric + */ + +import type IfNever from './if-never' +import type IsNegativeNumeric from './is-numeric-negative' + +/** + * Returns a type that indicates if `U` extends `NegativeNumeric`. + * + * @see {@linkcode IsNegativeNumeric} + * + * @example + * type X = IfNumeric<'-13', 1, 0> + * // 1 + * @example + * type X = IfNegativeNumeric + * // 1 + * @example + * type X = IfNegativeNumeric + * // 0 | 1 + * @example + * type X = IfNegativeNumeric<'13', 1, 0> + * // 0 + * @example + * type X = IfNegativeNumeric + * // 0 + * @example + * type X = IfNegativeNumeric + * // 0 + * @example + * type X = IfNegativeNumeric + * // 0 + * @example + * type X = IfNegativeNumeric + * // 0 + * @example + * type X = IfNegativeNumeric + * // 0 + * @example + * type X = IfNegativeNumeric + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `NegativeNumeric` + * @template F - Type if `U` does not extend `NegativeNumeric` + */ +type IfNegativeNumeric = IfNever< + U, + F, + U extends unknown ? (IsNegativeNumeric extends true ? T : F) : F +> + +export type { IfNegativeNumeric as default } diff --git a/src/types/if-numeric.ts b/src/types/if-numeric.ts index 70019280..c610e5b4 100644 --- a/src/types/if-numeric.ts +++ b/src/types/if-numeric.ts @@ -3,17 +3,53 @@ * @module tutils/types/IfNumeric */ +import type IfNever from './if-never' import type IsNumeric from './is-numeric' /** - * Returns a type that indicates if `T` is a `Numeric`. + * Returns a type that indicates if `U` extends `NegativeNumeric` or `Numeric`. * * @see {@linkcode IsNumeric} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `Numeric` - * @template False - Type if `T` is not a `Numeric` + * @example + * type X = IfNumeric<'-13', 1, 0> + * // 1 + * @example + * type X = IfNumeric<'13', 1, 0> + * // 1 + * @example + * type X = IfNumeric + * // 1 + * @example + * type X = IfNumeric + * // 1 + * @example + * type X = IfNumeric + * // 0 | 1 + * @example + * type X = IfNumeric + * // 0 + * @example + * type X = IfNumeric + * // 0 + * @example + * type X = IfNumeric + * // 0 + * @example + * type X = IfNumeric + * // 0 + * @example + * type X = IfNumeric + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `NegativeNumeric` or `Numeric` + * @template F - Type if `U` does not extend `NegativeNumeric` or `Numeric` */ -type IfNumeric = IsNumeric extends true ? True : False +type IfNumeric = IfNever< + U, + F, + U extends unknown ? (IsNumeric extends true ? T : F) : F +> export type { IfNumeric as default } diff --git a/src/types/if-object-curly.ts b/src/types/if-object-curly.ts index 6fa67264..0153e66b 100644 --- a/src/types/if-object-curly.ts +++ b/src/types/if-object-curly.ts @@ -3,19 +3,47 @@ * @module tutils/types/IfObjectCurly */ +import type IfNever from './if-never' import type IsObjectCurly from './is-object-curly' /** - * Returns a type that indicates if `T` is a curly-braced object. + * Returns a type that indicates if `U` extends `ObjectCurly`. * * @see {@linkcode IsObjectCurly} * - * @template T - Type to evaluate - * @template True - Type if `T` is curly-braced object - * @template False - Type if `T` is not curly-braced object + * @example + * type X = IfObjectCurly<{ hello: string; world: string }, 1, 0> + * // 1 + * @example + * type X = IfObjectCurly, 1, 0> + * // 0 | 1 + * @example + * type X = IfObjectCurly<(n: number) => string, 1, 0> + * // 0 + * @example + * type X = IfObjectCurly + * // 0 + * @example + * type X = IfObjectCurly + * // 0 + * @example + * type X = IfObjectCurly + * // 0 + * @example + * type X = IfObjectCurly + * // 0 + * @example + * type X = IfObjectCurly + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `ObjectCurly` + * @template F - Type if `U` does not extend `ObjectCurly` */ -type IfObjectCurly = IsObjectCurly extends true - ? True - : False +type IfObjectCurly = IfNever< + U, + F, + U extends unknown ? (IsObjectCurly extends true ? T : F) : F +> export type { IfObjectCurly as default } diff --git a/src/types/if-object-plain.ts b/src/types/if-object-plain.ts index 2db83b8d..46201288 100644 --- a/src/types/if-object-plain.ts +++ b/src/types/if-object-plain.ts @@ -3,19 +3,24 @@ * @module tutils/types/IfObjectPlain */ +import type IfNever from './if-never' import type IsObjectPlain from './is-object-plain' /** - * Returns a type that indicates if `T` is a plain object. + * Returns a type that indicates if `U` extends `ObjectPlain`. * * @see {@linkcode IsObjectPlain} * - * @template T - Type to evaluate - * @template True - Type if `T` is plain object - * @template False - Type if `T` is not plain object + * @todo examples + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `ObjectPlain` + * @template F - Type if `U` does not extend `ObjectPlain` */ -type IfObjectPlain = IsObjectPlain extends true - ? True - : False +type IfObjectPlain = IfNever< + U, + F, + U extends unknown ? (IsObjectPlain extends true ? T : F) : F +> export type { IfObjectPlain as default } diff --git a/src/types/if-object.ts b/src/types/if-object.ts index e8df2e9e..47d5537f 100644 --- a/src/types/if-object.ts +++ b/src/types/if-object.ts @@ -3,17 +3,47 @@ * @module tutils/types/IfObject */ +import type IfNever from './if-never' import type IsObject from './is-object' /** - * Returns a type that indicates if `T` is an `object`. + * Returns a type that indicates if `U` extends `object`. * * @see {@linkcode IsObject} * - * @template T - Type to evaluate - * @template True - Type if `T` is an `object` - * @template False - Type if `T` is not an `object` + * @example + * type X = IfObject<{ hello: string; world: string }, 1, 0> + * // 1 + * @example + * type X = IfObject<(n: number) => string, 1, 0> + * // 1 + * @example + * type X = IfObject + * // 1 + * @example + * type X = IfObject, 1, 0> + * // 0 | 1 + * @example + * type X = IfObject + * // 0 + * @example + * type X = IfObject + * // 0 + * @example + * type X = IfObject + * // 0 + * @example + * type X = IfObject + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `object` + * @template F - Type if `U` does not extend `object` */ -type IfObject = IsObject extends true ? True : False +type IfObject = IfNever< + U, + F, + U extends unknown ? (IsObject extends true ? T : F) : F +> export type { IfObject as default } diff --git a/src/types/if-primitive.ts b/src/types/if-primitive.ts index c7a4d999..71b7cb3c 100644 --- a/src/types/if-primitive.ts +++ b/src/types/if-primitive.ts @@ -3,17 +3,59 @@ * @module tutils/types/IfPrimitive */ +import type IfNever from './if-never' import type IsPrimitive from './is-primitive' /** - * Returns a type that indicates if `T` is a `Primitive`. + * Returns a type that indicates if `U` extends `Primitive`. * * @see {@linkcode IsPrimitive} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `Primitive` - * @template False - Type if `T` is not a `Primitive` + * @example + * type X = IfPrimitive + * // 1 + * @example + * type X = IfPrimitive + * // 1 + * @example + * type X = IfPrimitive + * // 1 + * @example + * type X = IfPrimitive + * // 1 + * @example + * type X = IfPrimitive + * // 1 + * @example + * type X = IfPrimitive + * // 1 + * @example + * type X = IfPrimitive + * // 1 + * @example + * type X = IfPrimitive, 1, 0> + * // 0 | 1 + * @example + * type X = IfPrimitive<{ hello: string; world: string }, 1, 0> + * // 0 + * @example + * type X = IfPrimitive + * // 0 + * @example + * type X = IfPrimitive + * // 0 + * @example + * type X = IfPrimitive + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `Primitive` + * @template F - Type if `U` does not extend `Primitive` */ -type IfPrimitive = IsPrimitive extends true ? True : False +type IfPrimitive = IfNever< + U, + F, + U extends unknown ? (IsPrimitive extends true ? T : F) : F +> export type { IfPrimitive as default } diff --git a/src/types/if-string.ts b/src/types/if-string.ts index a2052d2d..343c64f7 100644 --- a/src/types/if-string.ts +++ b/src/types/if-string.ts @@ -3,17 +3,41 @@ * @module tutils/types/IfString */ +import type IfNever from './if-never' import type IsString from './is-string' /** - * Returns a type that indicates if `T` is a `string`. + * Returns a type that indicates if `U` extends `string`. * * @see {@linkcode IsString} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `string` - * @template False - Type if `T` is not a `string` + * @example + * type X = IfString<'hello', 1, 0> + * // 1 + * @example + * type X = IfString + * // 1 + * @example + * type X = IfString + * // 0 + * @example + * type X = IfString + * // 0 + * @example + * type X = IfString + * // 0 + * @example + * type X = IfString + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `string` + * @template F - Type if `U` does not extend `string` */ -type IfString = IsString extends true ? True : False +type IfString = IfNever< + U, + F, + U extends unknown ? (IsString extends true ? T : F) : F +> export type { IfString as default } diff --git a/src/types/if-symbol.ts b/src/types/if-symbol.ts index b1d294c5..a1037a33 100644 --- a/src/types/if-symbol.ts +++ b/src/types/if-symbol.ts @@ -3,17 +3,44 @@ * @module tutils/types/IfSymbol */ +import type IfNever from './if-never' import type IsSymbol from './is-symbol' /** - * Returns a type that indicates if `T` is a `symbol`. + * Returns a type that indicates if `U` extends `symbol`. * * @see {@linkcode IsSymbol} * - * @template T - Type to evaluate - * @template True - Type if `T` is a `symbol` - * @template False - Type if `T` is not a `symbol` + * @example + * type X = IfSymbol + * // 1 + * @example + * type X = IfSymbol + * // 1 + * @example + * type X = IfSymbol + * // 0 | 1 + * @example + * type X = IfSymbol + * // 0 + * @example + * type X = IfSymbol + * // 0 + * @example + * type X = IfSymbol + * // 0 + * @example + * type X = IfSymbol + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `symbol` + * @template F - Type if `U` does not extend `symbol` */ -type IfSymbol = IsSymbol extends true ? True : False +type IfSymbol = IfNever< + U, + F, + U extends unknown ? (IsSymbol extends true ? T : F) : F +> export type { IfSymbol as default } diff --git a/src/types/if-true.ts b/src/types/if-true.ts new file mode 100644 index 00000000..7e1b42ca --- /dev/null +++ b/src/types/if-true.ts @@ -0,0 +1,46 @@ +/** + * @file Type Definitions - IfTrue + * @module tutils/types/IfTrue + */ + +import type IfNever from './if-never' +import type IsTrue from './is-true' + +/** + * Returns a type that indicates if `U` extends `true`. + * + * @see {@linkcode IsTrue} + * + * @example + * type X = IfTrue + * // 1 + * @example + * type X = IfTrue + * // 0 | 1 + * @example + * type X = IfTrue + * // 0 + * @example + * type X = IfTrue<1, 1, 0> + * // 0 + * @example + * type X = IfTrue + * // 0 + * @example + * type X = IfTrue + * // 0 + * @example + * type X = IfTrue + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `true` + * @template F - Type if `U` does not extend `true` + */ +type IfTrue = IfNever< + U, + F, + U extends unknown ? (IsTrue extends true ? T : F) : F +> + +export type { IfTrue as default } diff --git a/src/types/if-tuple.ts b/src/types/if-tuple.ts index eeb6fd8c..c4e5f869 100644 --- a/src/types/if-tuple.ts +++ b/src/types/if-tuple.ts @@ -3,19 +3,46 @@ * @module tutils/types/IfTuple */ +import type IfNever from './if-never' import type IsTuple from './is-tuple' /** - * Returns a type that indicates if `T` is a [tuple][1]. + * Returns a type that indicates if `U` extends a [tuple][1]. * * [1]: https://www.codecademy.com/resources/docs/typescript/tuples * * @see {@linkcode IsTuple} * - * @template T - Type to evaluate - * @template True - Type if `T` is a tuple - * @template False - Type if `T` is not a tuple + * @example + * type X = IfTuple + * // 1 + * @example + * type X = IfTuple + * // 1 + * @example + * type X = IfTuple[], 1, 0> + * // 0 | 1 + * @example + * type X = IfTuple[], 1, 0> + * // 0 + * @example + * type X = IfTuple + * // 0 + * @example + * type X = IfTuple + * // 0 + * @example + * type X = IfTuple + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends a tuple + * @template F - Type if `U` does not extend a tuple */ -type IfTuple = IsTuple extends true ? True : False +type IfTuple = IfNever< + U, + F, + U extends unknown ? (IsTuple extends true ? T : F) : F +> export type { IfTuple as default } diff --git a/src/types/if-undefined.ts b/src/types/if-undefined.ts index b1733b19..f42e24eb 100644 --- a/src/types/if-undefined.ts +++ b/src/types/if-undefined.ts @@ -3,17 +3,41 @@ * @module tutils/types/IfUndefined */ +import type IfNever from './if-never' import type IsUndefined from './is-undefined' /** - * Returns a type that indicates if `T` is `undefined`. + * Returns a type that indicates if `U` extends `undefined`. * * @see {@linkcode IsUndefined} * - * @template T - Type to evaluate - * @template True - Type if `T` is `undefined` - * @template False - Type if `T` is not `undefined` + * @example + * type X = IfUndefined + * // 1 + * @example + * type X = IfUndefined + * // 0 | 1 + * @example + * type X = IfUndefined + * // 0 + * @example + * type X = IfUndefined + * // 0 + * @example + * type X = IfUndefined + * // 0 + * @example + * type X = IfUndefined + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` extends `undefined` + * @template F - Type if `U` does not extend `undefined` */ -type IfUndefined = IsUndefined extends true ? True : False +type IfUndefined = IfNever< + U, + F, + U extends unknown ? (IsUndefined extends true ? T : F) : F +> export type { IfUndefined as default } diff --git a/src/types/if-unknown.ts b/src/types/if-unknown.ts index acb04195..af168ec8 100644 --- a/src/types/if-unknown.ts +++ b/src/types/if-unknown.ts @@ -6,14 +6,27 @@ import type IsUnknown from './is-unknown' /** - * Returns a type that indicates if `T` is `unknown`. + * Returns a type that indicates if `U` is `unknown`. * * @see {@linkcode IsUnknown} * - * @template T - Type to evaluate - * @template True - Type if `T` is `unknown` - * @template False - Type if `T` is not `unknown` + * @example + * type X = IfUnknown + * // 1 + * @example + * type X = IfUnknown + * // 0 + * @example + * type X = IfUnknown + * // 0 + * @example + * type X = IfUnknown<{ hello: string; world: string }, 1, 0> + * // 0 + * + * @template U - Type to evaluate + * @template T - Type if `U` is `unknown` + * @template F - Type if `U` is not `unknown` */ -type IfUnknown = IsUnknown extends true ? True : False +type IfUnknown = IsUnknown extends true ? T : F export type { IfUnknown as default } diff --git a/src/types/index.ts b/src/types/index.ts index 9622cf84..e3277458 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,6 +3,7 @@ * @module tutils/types */ +export type { default as ArrayToUnion } from './array-to-union' export type { default as Assign } from './assign' export type { default as At } from './at' export type { default as Booleanish } from './booleanish' @@ -15,11 +16,11 @@ export type { default as Constructor } from './constructor' export type { default as AbstractConstructor } from './constructor-abstract' export type { default as ContinuousValue } from './continuous-value' export type { default as Digit } from './digit' +export type { default as Dot } from './dot' export type { default as EmptyArray } from './empty-array' export type { default as EmptyObject } from './empty-object' export type { default as EmptyString } from './empty-string' export type { default as EmptyValue } from './empty-value' -export type { default as EnsureString } from './ensure-string' export type { default as ExactOptionalPropertyTypes } from './exact-optional-property-types' export type { default as Fallback } from './fallback' export type { default as Falsy } from './falsy' @@ -27,6 +28,7 @@ export type { default as FIXME } from './fixme' export type { default as Float } from './float' export type { default as Fn } from './fn' export type { default as Get } from './get' +export type { default as HasKeys } from './has-keys' export type { default as Head } from './head' export type { default as IfAny } from './if-any' export type { default as IfAnyOrNever } from './if-any-or-never' @@ -34,49 +36,69 @@ export type { default as IfArray } from './if-array' export type { default as IfBigInt } from './if-big-int' export type { default as IfBoolean } from './if-boolean' export type { default as IfEqual } from './if-equal' +export type { default as IfFalse } from './if-false' export type { default as IfFunction } from './if-function' +export type { default as IfIndexSignature } from './if-index-signature' +export type { default as IfInteger } from './if-integer' +export type { default as IfNegativeInteger } from './if-integer-negative' 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 IfReadonlyKey } from './if-key-readonly' export type { default as IfRequiredKey } from './if-key-required' +export type { default as IfKeys } from './if-keys' +export type { default as IfLiteral } from './if-literal' +export type { default as IfNegative } from './if-negative' export type { default as IfNever } from './if-never' export type { default as IfNil } from './if-nil' export type { default as IfNull } from './if-null' export type { default as IfNumber } from './if-number' export type { default as IfNumeric } from './if-numeric' +export type { default as IfNegativeNumeric } from './if-numeric-negative' export type { default as IfObject } from './if-object' export type { default as IfObjectCurly } from './if-object-curly' export type { default as IfObjectPlain } from './if-object-plain' export type { default as IfPrimitive } from './if-primitive' export type { default as IfString } from './if-string' export type { default as IfSymbol } from './if-symbol' +export type { default as IfTrue } from './if-true' export type { default as IfTuple } from './if-tuple' export type { default as IfUndefined } from './if-undefined' export type { default as IfUnknown } from './if-unknown' export type { default as Indices } from './indices' export type { default as Integer } from './integer' +export type { default as Intersection } from './intersection' export type { default as IsAny } from './is-any' export type { default as IsAnyOrNever } from './is-any-or-never' export type { default as IsArray } from './is-array' export type { default as IsBigInt } from './is-big-int' export type { default as IsBoolean } from './is-boolean' export type { default as IsEqual } from './is-equal' +export type { default as IsFalse } from './is-false' export type { default as IsFunction } from './is-function' +export type { default as IsIndexSignature } from './is-index-signature' +export type { default as IsInteger } from './is-integer' +export type { default as IsNegativeInteger } from './is-integer-negative' 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 IsReadonlyKey } from './is-key-readonly' export type { default as IsRequiredKey } from './is-key-required' +export type { default as IsLiteral } from './is-literal' +export type { default as IsNegative } from './is-negative' export type { default as IsNever } from './is-never' export type { default as IsNil } from './is-nil' export type { default as IsNull } from './is-null' export type { default as IsNumber } from './is-number' export type { default as IsNumeric } from './is-numeric' +export type { default as IsNegativeNumeric } from './is-numeric-negative' export type { default as IsObject } from './is-object' export type { default as IsObjectCurly } from './is-object-curly' export type { default as IsObjectPlain } from './is-object-plain' export type { default as IsPrimitive } from './is-primitive' export type { default as IsString } from './is-string' export type { default as IsSymbol } from './is-symbol' +export type { default as IsTrue } from './is-true' export type { default as IsTuple } from './is-tuple' export type { default as IsUndefined } from './is-undefined' export type { default as IsUnknown } from './is-unknown' @@ -90,22 +112,23 @@ export type { default as Jsonifiable } from './jsonifiable' export type { default as JsonifiableArray } from './jsonifiable-array' export type { default as JsonifiableInstance } from './jsonifiable-instance' export type { default as JsonifiableObject } from './jsonifiable-object' -export type { default as Keys } from './keys' +export type { default as Keyof } from './keyof' export type { default as OptionalKeys } from './keys-optional' +export type { default as ReadonlyKeys } from './keys-readonly' export type { default as RequiredKeys } from './keys-required' +export type { default as Length } from './length' export type { default as LiteralUnion } from './literal-union' export type { default as Merge } from './merge' -export type { default as MergeDefaults } from './merge-defaults' export type { default as NIL } from './nil' export type { default as Nilable } from './nilable' export type { default as Nullable } from './nullable' export type { default as NumberLike } from './number-like' export type { default as NumberString } from './number-string' export type { default as Numeric } from './numeric' +export type { default as NegativeNumeric } from './numeric-negative' export type { default as ObjectCurly } from './object-curly' export type { default as ObjectPlain } from './object-plain' -export type { default as Omit } from './omit' -export type { default as OmitNative } from './omit-native' +export type { default as OmitIndexSignature } from './omit-index-signature' export type { default as OneOrMany } from './one-or-many' export type { default as Opaque } from './opaque' export type { default as Optional } from './optional' @@ -113,13 +136,12 @@ export type { default as OrLowercase } from './or-lowercase' export type { default as OrUppercase } from './or-uppercase' export type { default as Overwrite } from './overwrite' export type { default as Path } from './path' -export type { default as Pick } from './pick' -export type { default as PickNative } from './pick-native' export type { default as Predicate } from './predicate' export type { default as Primitive } from './primitive' export type { default as Promisable } from './promisable' export type { default as PropertyKey } from './property-key' export type { default as NaturalRange } from './range-natural' +export type { default as Reverse } from './reverse' export type { default as Sift } from './sift' export type { default as Simplify } from './simplify' export type { default as Split } from './split' @@ -133,9 +155,8 @@ export type { default as Trim } from './trim' export type { default as TrimEnd } from './trim-end' export type { default as TrimStart } from './trim-start' export type { default as Tryit } from './tryit' -export type { default as TupleLength } from './tuple-length' -export type { default as TupleToUnion } from './tuple-to-union' export type { default as TypedArray } from './typed-array' export type { default as UnionToIntersection } from './union-to-intersection' export type { default as UnionToTuple } from './union-to-tuple' +export type { default as UnwrapNumeric } from './unwrap-numeric' export type { default as Whitespace } from './whitespace' diff --git a/src/types/indices.ts b/src/types/indices.ts index dc016673..385d1888 100644 --- a/src/types/indices.ts +++ b/src/types/indices.ts @@ -4,32 +4,60 @@ */ import type EmptyArray from './empty-array' -import type IfArray from './if-array' -import type IfTuple from './if-tuple' -import type Numeric from './numeric' +import type EmptyString from './empty-string' +import type Length from './length' +import type NaturalRange from './range-natural' +import type Subtract from './subtract' +import type UnwrapNumeric from './unwrap-numeric' /** - * Constructs a union of array indices. + * Constructs a union of indices. * + * **Note**: Includes negative indices. + * + * @example + * type X = Indices<['a', 'b', 'c'?]> + * // -1 | -2 | -3 | 0 | 1 | 2 + * @example + * type X = Indices<'abc'> + * // -1 | -2 | -3 | 0 | 1 | 2 + * @example + * type X = Indices + * // number + * @example + * type X = Indices + * // number + * @example + * type X = Indices + * // number + * @example + * type X = Indices + * // never + * @example + * type X = Indices + * // never * @example - * Indices<['a', 'b']> // '0' | '1' - * Indices // number | `${number}` - * Indices<{ a: string[] }> // never - * Indices // never + * type X = Indices> + * // never * * @template T - Type to evaluate */ -type Indices = IfArray< - NonNullable, - unknown, - NonNullable extends EmptyArray +type Indices = ( + T extends EmptyString | Readonly ? never - : IfTuple< - NonNullable, - Extract, Numeric>, - Numeric | number - >, - never -> + : Length extends infer L extends number + ? number extends L + ? L + : NaturalRange extends infer R extends number + ? Length> extends infer L extends number + ? { + [K in R]: K | UnwrapNumeric}`, '-0'>> + }[R] + : never + : never + : never +) extends infer I extends number + ? I + : never export type { Indices as default } diff --git a/src/types/intersection.ts b/src/types/intersection.ts new file mode 100644 index 00000000..68d708f4 --- /dev/null +++ b/src/types/intersection.ts @@ -0,0 +1,27 @@ +/** + * @file Type Definitions - Intersection + * @module tutils/types/Intersection + */ + +import type IfEqual from './if-equal' + +/** + * Returns the intersection of `T` and `U`. + * + * @example + * type X = Intersection<-1 | -2 | -3 | 0 | 1 | 2, -3 | 0> + * // -3 | 0 + * @example + * type X = Intersection + * // never + * + * @template T - First type to evaluate + * @template U - Second type to evaluate + */ +type Intersection = T extends unknown + ? U extends unknown + ? IfEqual + : never + : never + +export type { Intersection as default } diff --git a/src/types/is-any-or-never.ts b/src/types/is-any-or-never.ts index 84a93525..0bc49d36 100644 --- a/src/types/is-any-or-never.ts +++ b/src/types/is-any-or-never.ts @@ -9,6 +9,22 @@ import type IfNever from './if-never' /** * Returns a boolean indicating if `T` is `any` or `never`. * + * @example + * type X = IsAnyOrNever + * // true + * @example + * type X = IsAnyOrNever + * // true + * @example + * type X = IsAnyOrNever + * // false + * @example + * type X = IsAnyOrNever + * // false + * @example + * type X = IsAnyOrNever + * // false + * * @template T - Type to evaluate */ type IsAnyOrNever = IfAny> diff --git a/src/types/is-any.ts b/src/types/is-any.ts index 5de6ad23..e3ed1955 100644 --- a/src/types/is-any.ts +++ b/src/types/is-any.ts @@ -6,6 +6,19 @@ /** * Returns a boolean indicating if `T` is `any`. * + * @example + * type X = IsAny + * // true + * @example + * type X = IsAny + * // false + * @example + * type X = IsAny + * // false + * @example + * type X = IsAny + * // false + * * @template T - Type to evaluate */ type IsAny = 0 extends T & 1 ? true : false diff --git a/src/types/is-array.ts b/src/types/is-array.ts index e9c16386..dccef8df 100644 --- a/src/types/is-array.ts +++ b/src/types/is-array.ts @@ -6,7 +6,38 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is an array of type `V`. + * Returns a boolean indicating if `T` extends an array of type `V`. + * + * @example + * type X = IsArray<['a', 'b', 'c'?]> + * // true + * @example + * type X = IsArray + * // true + * @example + * type X = IsArray<['a', 'b', 'c'?], number> + * // false + * @example + * type X = IsArray + * // false + * @example + * type X = IsArray> + * // boolean + * @example + * type X = IsArray> + * // boolean + * @example + * type X = IsArray + * // false + * @example + * type X = IsArray + * // false + * @example + * type X = IsArray + * // false + * @example + * type X = IsArray + * // false * * @template T - Type to evaluate * @template V - Array element type @@ -14,7 +45,7 @@ import type IfAnyOrNever from './if-any-or-never' type IsArray = IfAnyOrNever< T, false, - [T] extends [readonly V[]] ? true : false + T extends readonly V[] ? true : false > export type { IsArray as default } diff --git a/src/types/is-big-int.ts b/src/types/is-big-int.ts index 0b5d2a99..4c4a37fd 100644 --- a/src/types/is-big-int.ts +++ b/src/types/is-big-int.ts @@ -6,10 +6,32 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is a `bigint`. + * Returns a boolean indicating if `T` extends `bigint`. + * + * @example + * type X = IsBigInt<1n> + * // true + * @example + * type X = IsBigInt + * // true + * @example + * type X = IsBigInt + * // boolean + * @example + * type X = IsBigInt + * // false + * @example + * type X = IsBigInt + * // false + * @example + * type X = IsBigInt + * // false + * @example + * type X = IsBigInt + * // false * * @template T - Type to evaluate */ -type IsBigInt = IfAnyOrNever +type IsBigInt = IfAnyOrNever export type { IsBigInt as default } diff --git a/src/types/is-boolean.ts b/src/types/is-boolean.ts index 15269099..cd3225e3 100644 --- a/src/types/is-boolean.ts +++ b/src/types/is-boolean.ts @@ -6,10 +6,32 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is a `boolean`. + * Returns a boolean indicating if `T` extends `boolean`. + * + * @example + * type X = IsBoolean + * // true + * @example + * type X = IsBoolean + * // true + * @example + * type X = IsBoolean + * // true + * @example + * type X = IsBoolean<1 | true> + * // boolean + * @example + * type X = IsBoolean + * // false + * @example + * type X = IsBoolean + * // false + * @example + * type X = IsBoolean + * // false * * @template T - Type to evaluate */ -type IsBoolean = IfAnyOrNever +type IsBoolean = IfAnyOrNever export type { IsBoolean as default } diff --git a/src/types/is-equal.ts b/src/types/is-equal.ts index a28ce86e..04b9962d 100644 --- a/src/types/is-equal.ts +++ b/src/types/is-equal.ts @@ -6,8 +6,32 @@ /** * Returns a boolean indicating if `A` and `B` are equal. * - * @see https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650 - * @see https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796 + * @see https://github.com/microsoft/TypeScript/issues/27024 + * + * @example + * type X = IsEqual + * // true + * @example + * type X = IsEqual<3 | 13, 3 | 13> + * // true + * @example + * type X = IsEqual<['a', 'b'?], ['a', 'b'?]> + * // true + * @example + * type X = IsEqual + * // true + * @example + * type X = IsEqual + * // false + * @example + * type X = IsEqual<1 | 3, 3 | 13> + * // false + * @example + * type X = IsEqual<['a', 'b'?], readonly ['a', 'b'?]> + * // false + * @example + * type X = IsEqual<() => string, (n: number) => string> + * // false * * @template A - First type to evaluate * @template B - Second type to evaluate diff --git a/src/types/is-false.ts b/src/types/is-false.ts new file mode 100644 index 00000000..520ea693 --- /dev/null +++ b/src/types/is-false.ts @@ -0,0 +1,34 @@ +/** + * @file Type Definitions - IsFalse + * @module tutils/types/IsFalse + */ + +import type IfAnyOrNever from './if-any-or-never' + +/** + * Returns a boolean indicating if `T` extends `false`. + * + * @example + * type X = IsFalse + * // true + * @example + * type X = IsFalse + * // boolean + * @example + * type X = IsFalse + * // false + * @example + * type X = IsFalse + * // false + * @example + * type X = IsFalse + * // false + * @example + * type X = IsFalse + * // false + * + * @template T - Type to evaluate + */ +type IsFalse = IfAnyOrNever + +export type { IsFalse as default } diff --git a/src/types/is-function.ts b/src/types/is-function.ts index 82601f87..76c90844 100644 --- a/src/types/is-function.ts +++ b/src/types/is-function.ts @@ -7,12 +7,42 @@ import type Fn from './fn' import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is a function. + * Returns a boolean indicating if `T` extends {@linkcode Fn}. * - * @see {@linkcode Fn} + * @example + * type X = IsFunction + * // true + * @example + * type X = IsFunction + * // true + * @example + * type X = IsFunction> + * // true + * @example + * type X = IsFunction + * // true + * @example + * type X = IsFunction<(n: number) => string> + * // true + * @example + * type X = IsFunction> + * // boolean + * @example + * type X = IsFunction + * // false + * @example + * type X = IsFunction + * // false + * @example + * type X = IsFunction + * // false * * @template T - Type to evaluate */ -type IsFunction = IfAnyOrNever +type IsFunction = IfAnyOrNever< + T, + false, + T extends Readonly ? true : false +> export type { IsFunction as default } diff --git a/src/types/is-index-signature.ts b/src/types/is-index-signature.ts new file mode 100644 index 00000000..5c7b1dba --- /dev/null +++ b/src/types/is-index-signature.ts @@ -0,0 +1,53 @@ +/** + * @file Type Definitions - IsIndexSignature + * @module tutils/types/IsIndexSignature + */ + +import type IfAnyOrNever from './if-any-or-never' +import type IfEqual from './if-equal' +import type IfNever from './if-never' +import type Opaque from './opaque' +import type Primitive from './primitive' +import type PropertyKey from './property-key' + +/** + * Returns a boolean indicating if `K` is an index signature property of `T`. + * + * @see https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures + * + * @todo examples + * + * @template T - Type to evaluate + * @template K - Key to evaluate + */ +type IsIndexSignature = IfNever< + T, + false, + IfAnyOrNever< + K, + false, + T extends unknown + ? K extends PropertyKey + ? { + [H in keyof (T extends Opaque + ? T + : T extends NonNullable + ? Opaque + : T) as H extends keyof Object + ? never + : {} extends Record + ? K extends H + ? IfEqual + : never + : never]: H + } extends infer X + ? K extends keyof X + ? true + : false + : never + : false + : never + > +> + +export type { IsIndexSignature as default } diff --git a/src/types/is-integer-negative.ts b/src/types/is-integer-negative.ts new file mode 100644 index 00000000..ea63d5b2 --- /dev/null +++ b/src/types/is-integer-negative.ts @@ -0,0 +1,49 @@ +/** + * @file Type Definitions - IsNegativeInteger + * @module tutils/types/IsNegativeInteger + */ + +import type IfAnyOrNever from './if-any-or-never' +import type IfInteger from './if-integer' +import type IsNegative from './is-negative' + +/** + * Returns a boolean indicating if `T` extends a negative integer. + * + * @example + * type X = IsNegativeInteger<-1> + * // true + * @example + * type X = IsNegativeInteger<-1 | 1> + * // boolean + * @example + * type X = IsNegativeInteger<-1n> + * // false + * @example + * type X = IsNegativeInteger<0> + * // false + * @example + * type X = IsNegativeInteger<1> + * // false + * @example + * type X = IsNegativeInteger + * // false + * @example + * type X = IsNegativeInteger + * // false + * @example + * type X = IsNegativeInteger + * // false + * @example + * type X = IsNegativeInteger + * // false + * + * @template T - Type to evaluate + */ +type IsNegativeInteger = IfAnyOrNever< + T, + false, + T extends number ? IfInteger, false> : false +> + +export type { IsNegativeInteger as default } diff --git a/src/types/is-integer.ts b/src/types/is-integer.ts new file mode 100644 index 00000000..72d80022 --- /dev/null +++ b/src/types/is-integer.ts @@ -0,0 +1,61 @@ +/** + * @file Type Definitions - IsInteger + * @module tutils/types/IsInteger + */ + +import type IfAnyOrNever from './if-any-or-never' +import type Integer from './integer' +import type IsNumber from './is-number' +import type Stringify from './stringify' + +/** + * Returns a boolean indicating if `T` extends an integer. + * + * @example + * type X = IsInteger<-1> + * // true + * @example + * type X = IsInteger<0> + * // true + * @example + * type X = IsInteger<1> + * // true + * @example + * type X = IsInteger + * // true + * @example + * type X = IsInteger<-1 | 1n> + * // boolean + * @example + * type X = IsInteger + * // false + * @example + * type X = IsInteger + * // false + * @example + * type X = IsInteger + * // false + * @example + * type X = IsInteger + * // false + * @example + * type X = IsInteger + * // false + * + * @template T - Type to evaluate + */ +type IsInteger = IfAnyOrNever< + T, + false, + T extends Integer + ? true + : IsNumber extends true + ? T extends symbol + ? false + : Stringify extends Stringify + ? true + : false + : false +> + +export type { IsInteger as default } diff --git a/src/types/is-json-primitive.ts b/src/types/is-json-primitive.ts index 28d3e888..d0633018 100644 --- a/src/types/is-json-primitive.ts +++ b/src/types/is-json-primitive.ts @@ -7,14 +7,48 @@ import type IfAnyOrNever from './if-any-or-never' import type JsonPrimitive from './json-primitive' /** - * Returns a boolean indicating if `T` is a {@linkcode JsonPrimitive}. + * Returns a boolean indicating if `T` extends {@linkcode JsonPrimitive}. + * + * @example + * type X = IsJsonPrimitive + * // true + * @example + * type X = IsJsonPrimitive + * // true + * @example + * type X = IsJsonPrimitive + * // true + * @example + * type X = IsJsonPrimitive + * // true + * @example + * type X = IsJsonPrimitive + * // true + * @example + * type X = IsJsonPrimitive> + * // boolean + * @example + * type X = IsJsonPrimitive + * // false + * @example + * type X = IsJsonPrimitive + * // false + * @example + * type X = IsJsonPrimitive + * // false + * @example + * type X = IsJsonPrimitive + * // false + * @example + * type X = IsJsonPrimitive + * // false * * @template T - Type to evaluate */ type IsJsonPrimitive = IfAnyOrNever< T, false, - [T] extends [JsonPrimitive] ? true : false + T extends JsonPrimitive ? true : false > export type { IsJsonPrimitive as default } diff --git a/src/types/is-key-optional-exact.ts b/src/types/is-key-optional-exact.ts index a04c09f5..f4096a43 100644 --- a/src/types/is-key-optional-exact.ts +++ b/src/types/is-key-optional-exact.ts @@ -3,42 +3,47 @@ * @module tutils/types/IsExactOptionalKey */ +import type Dot from './dot' import type Get from './get' import type IfAnyOrNever from './if-any-or-never' -import type IfEqual from './if-equal' -import type IfOptionalKey from './if-key-optional' -import type Optional from './optional' +import type IfNever from './if-never' +import type ExactOptionalKeys from './keys-optional-exact' +import type Numeric from './numeric' import type PropertyKey from './property-key' +import type UnwrapNumeric from './unwrap-numeric' /** * Returns a boolean indicating if `K` is an exact optional property of `T`. * * Supports dot-notation for nested paths and array indexing. * - * @see https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes + * @see {@linkcode ExactOptionalKeys} + * + * @todo examples * * @template T - Type to evaluate - * @template K - Key to evaluate + * @template K - Keys to evaluate */ -type IsExactOptionalKey = IfAnyOrNever< - K, +type IsExactOptionalKey = IfNever< + T, false, - ( - K extends `${infer H}.${infer Rest}` - ? IsExactOptionalKey - : NonNullable extends infer U - ? IfOptionalKey< - U, - K, - Get, K> extends infer V - ? IfEqual, false, true> - : false, - false - > + IfAnyOrNever< + K, + false, + T extends unknown + ? K extends `${infer H}${Dot}${infer R}` + ? IsExactOptionalKey>, R> + : ExactOptionalKeys extends infer Q extends keyof T + ? K extends Q + ? true + : K extends Numeric + ? UnwrapNumeric extends Q + ? true + : false + : false + : never : false - ) extends infer R - ? IfEqual - : never + > > export type { IsExactOptionalKey as default } diff --git a/src/types/is-key-optional.ts b/src/types/is-key-optional.ts index 853b8778..acdc4b0a 100644 --- a/src/types/is-key-optional.ts +++ b/src/types/is-key-optional.ts @@ -3,32 +3,47 @@ * @module tutils/types/IsOptionalKey */ +import type Dot from './dot' import type Get from './get' import type IfAnyOrNever from './if-any-or-never' -import type IfEqual from './if-equal' +import type IfNever from './if-never' import type OptionalKeys from './keys-optional' +import type Numeric from './numeric' import type PropertyKey from './property-key' +import type UnwrapNumeric from './unwrap-numeric' /** * Returns a boolean indicating if `K` is an optional property of `T`. * * Supports dot-notation for nested paths and array indexing. * + * @see {@linkcode OptionalKeys} + * + * @todo examples + * * @template T - Type to evaluate - * @template K - Key to evaluate + * @template K - Keys to evaluate */ -type IsOptionalKey = IfAnyOrNever< - K, +type IsOptionalKey = IfNever< + T, false, - ( - K extends `${infer H}.${infer Rest}` - ? IsOptionalKey, Rest> - : K extends OptionalKeys - ? true + IfAnyOrNever< + K, + false, + T extends unknown + ? K extends `${infer H}${Dot}${infer R}` + ? IsOptionalKey>, R> + : OptionalKeys extends infer Q extends keyof T + ? K extends Q + ? true + : K extends Numeric + ? UnwrapNumeric extends Q + ? true + : false + : false + : never : false - ) extends infer R - ? IfEqual - : never + > > export type { IsOptionalKey as default } diff --git a/src/types/is-key-readonly.ts b/src/types/is-key-readonly.ts new file mode 100644 index 00000000..bd04b0d7 --- /dev/null +++ b/src/types/is-key-readonly.ts @@ -0,0 +1,49 @@ +/** + * @file Type Definitions - IsReadonlyKey + * @module tutils/types/IsReadonlyKey + */ + +import type Dot from './dot' +import type Get from './get' +import type IfAnyOrNever from './if-any-or-never' +import type IfNever from './if-never' +import type ReadonlyKeys from './keys-readonly' +import type Numeric from './numeric' +import type PropertyKey from './property-key' +import type UnwrapNumeric from './unwrap-numeric' + +/** + * Returns a boolean indicating if `K` is a readonly property of `T`. + * + * Supports dot-notation for nested paths and array indexing. + * + * @see {@linkcode ReadonlyKeys} + * + * @todo examples + * + * @template T - Type to evaluate + * @template K - Keys to evaluate + */ +type IsReadonlyKey = IfNever< + T, + false, + IfAnyOrNever< + K, + false, + T extends unknown + ? K extends `${infer H}${Dot}${infer R}` + ? IsReadonlyKey>, R> + : ReadonlyKeys extends infer Q extends keyof T + ? K extends Q + ? true + : K extends Numeric + ? UnwrapNumeric extends Q + ? true + : false + : false + : never + : false + > +> + +export type { IsReadonlyKey as default } diff --git a/src/types/is-key-required.ts b/src/types/is-key-required.ts index baf0cff8..16a3ba02 100644 --- a/src/types/is-key-required.ts +++ b/src/types/is-key-required.ts @@ -3,32 +3,53 @@ * @module tutils/types/IsRequiredKey */ +import type Dot from './dot' import type Get from './get' import type IfAnyOrNever from './if-any-or-never' -import type IfEqual from './if-equal' +import type IfIndexSignature from './if-index-signature' +import type IfNever from './if-never' import type RequiredKeys from './keys-required' +import type Numeric from './numeric' import type PropertyKey from './property-key' +import type UnwrapNumeric from './unwrap-numeric' /** * Returns a boolean indicating if `K` is a required property of `T`. * * Supports dot-notation for nested paths and array indexing. * + * @see {@linkcode RequiredKeys} + * + * @todo examples + * * @template T - Type to evaluate - * @template K - Key to evaluate + * @template K - Keys to evaluate */ -type IsRequiredKey = IfAnyOrNever< - K, +type IsRequiredKey = IfNever< + T, false, - ( - K extends `${infer H}.${infer Rest}` - ? IsRequiredKey, Rest> - : K extends RequiredKeys - ? true + IfAnyOrNever< + K, + false, + T extends unknown + ? K extends `${infer H}${Dot}${infer R}` + ? IsRequiredKey>, R> + : IfIndexSignature< + T, + K, + true, + RequiredKeys extends infer Q extends keyof T + ? K extends Q + ? true + : K extends Numeric + ? UnwrapNumeric extends Q + ? true + : false + : false + : never + > : false - ) extends infer R - ? IfEqual - : never + > > export type { IsRequiredKey as default } diff --git a/src/types/is-literal.ts b/src/types/is-literal.ts new file mode 100644 index 00000000..9598123b --- /dev/null +++ b/src/types/is-literal.ts @@ -0,0 +1,98 @@ +/** + * @file Type Definitions - IsLiteral + * @module tutils/types/IsLiteral + */ + +import type IfAnyOrNever from './if-any-or-never' +import type IfFalse from './if-false' +import type IfNever from './if-never' +import type IfNil from './if-nil' +import type IfTrue from './if-true' +import type Primitive from './primitive' + +/** + * Returns a boolean indicating if `T` extends a literal {@linkcode Primitive}. + * + * @internal + * + * @template T - Type to evaluate + * @template P - Primitive value type + * @template U - Copy of pre-distributed `T` + */ +type LiteralCheck = IfAnyOrNever< + T, + false, + T extends object + ? false + : T extends Extract + ? Extract extends infer X + ? IfNever< + X, + true, + IfNil< + X, + true, + boolean extends U ? false : IfFalse> + > + > + : never + : false +> + +/** + * Returns a boolean indicating if `T` extends a literal {@linkcode Primitive}. + * + * @see https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types + * + * @example + * type X = IsLiteral<3n> + * // true + * @example + * type X = IsLiteral<3n, bigint> + * // true + * @example + * type X = IsLiteral + * // true + * @example + * type X = IsLiteral + * // true + * @example + * type X = IsLiteral<3> + * // true + * @example + * type X = IsLiteral<3, number> + * // true + * @example + * type X = IsLiteral<'msg'> + * // true + * @example + * type X = IsLiteral<'msg', string> + * // true + * @example + * type X = IsLiteral + * // true + * @example + * type X = IsLiteral + * // boolean + * @example + * type X = IsLiteral> + * // false + * @example + * type X = IsLiteral<3 & { id: string }> + * // false + * @example + * type X = IsLiteral + * // false + * @example + * type X = IsLiteral + * // false + * @example + * type X = IsLiteral + * // false + * + * @template T - Type to evaluate + * @template P - Primitive value type + */ +type IsLiteral = LiteralCheck + +export type { IsLiteral as default } diff --git a/src/types/is-negative.ts b/src/types/is-negative.ts new file mode 100644 index 00000000..3395723d --- /dev/null +++ b/src/types/is-negative.ts @@ -0,0 +1,73 @@ +/** + * @file Type Definitions - IsNegative + * @module tutils/types/IsNegative + */ + +import type IfAnyOrNever from './if-any-or-never' +import type IsNegativeNumeric from './is-numeric-negative' +import type NumberLike from './number-like' +import type Stringify from './stringify' + +/** + * Returns a boolean indicating if `T` extends a negative value. + * + * Negative values include: + * + * - `NegativeNumeric` + * - A negative `bigint` (e.g. `-1n`) + * - A negative `number` (e.g. `-1`) + * + * @see {@linkcode IsNegativeNumeric} + * + * @example + * type X = IsNegative<-1> + * // true + * @example + * type X = IsNegative<-1n> + * // true + * @example + * type X = IsNegative<'-1'> + * // true + * @example + * type X = IsNegative<-1 | -1n | '-1' | 1> + * // boolean + * @example + * type X = IsNegative<0> + * // false + * @example + * type X = IsNegative<1> + * // false + * @example + * type X = IsNegative<0n> + * // false + * @example + * type X = IsNegative<'1'> + * // false + * @example + * type X = IsNegative + * // false + * @example + * type X = IsNegative + * // false + * @example + * type X = IsNegative + * // false + * @example + * type X = IsNegative + * // false + * @example + * type X = IsNegative + * // false + * @example + * type X = IsNegative + * // false + * + * @template T - Type to evaluate + */ +type IsNegative = IfAnyOrNever< + T, + false, + T extends NumberLike | bigint ? IsNegativeNumeric> : false +> + +export type { IsNegative as default } diff --git a/src/types/is-never.ts b/src/types/is-never.ts index 6f53598c..9c48f676 100644 --- a/src/types/is-never.ts +++ b/src/types/is-never.ts @@ -6,6 +6,19 @@ /** * Returns a boolean indicating if `T` is `never`. * + * @example + * type X = IsNever + * // true + * @example + * type X = IsNever + * // false + * @example + * type X = IsNever + * // false + * @example + * type X = IsNever + * // false + * * @template T - Type to evaluate */ type IsNever = [T] extends [never] ? true : false diff --git a/src/types/is-nil.ts b/src/types/is-nil.ts index e0cc712f..91fdf37c 100644 --- a/src/types/is-nil.ts +++ b/src/types/is-nil.ts @@ -7,10 +7,32 @@ import type IfAnyOrNever from './if-any-or-never' import type NIL from './nil' /** - * Returns a boolean indicating if `T` is {@linkcode NIL}. + * Returns a boolean indicating if `T` extends {@linkcode NIL}. + * + * @example + * type X = IsNil + * // true + * @example + * type X = IsNil + * // true + * @example + * type X = IsNil + * // true + * @example + * type X = IsNil> + * // boolean + * @example + * type X = IsNil + * // false + * @example + * type X = IsNil + * // false + * @example + * type X = IsNil + * // false * * @template T - Type to evaluate */ -type IsNil = IfAnyOrNever +type IsNil = IfAnyOrNever export type { IsNil as default } diff --git a/src/types/is-null.ts b/src/types/is-null.ts index ebd402bb..625bb2ae 100644 --- a/src/types/is-null.ts +++ b/src/types/is-null.ts @@ -6,10 +6,26 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is `null`. + * Returns a boolean indicating if `T` extends `null`. + * + * @example + * type X = IsNull + * // true + * @example + * type X = IsNull + * // boolean + * @example + * type X = IsNull + * // false + * @example + * type X = IsNull + * // false + * @example + * type X = IsNull + * // false * * @template T - Type to evaluate */ -type IsNull = IfAnyOrNever +type IsNull = IfAnyOrNever export type { IsNull as default } diff --git a/src/types/is-number.ts b/src/types/is-number.ts index 063f387e..beec4133 100644 --- a/src/types/is-number.ts +++ b/src/types/is-number.ts @@ -6,10 +6,32 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is a `number`. + * Returns a boolean indicating if `T` extends `number`. + * + * @example + * type X = IsNumber<1> + * // true + * @example + * type X = IsNumber + * // true + * @example + * type X = IsNumber> + * // true + * @example + * type X = IsNumber> + * // boolean + * @example + * type X = IsNumber + * // false + * @example + * type X = IsNumber + * // false + * @example + * type X = IsNumber + * // false * * @template T - Type to evaluate */ -type IsNumber = IfAnyOrNever +type IsNumber = IfAnyOrNever export type { IsNumber as default } diff --git a/src/types/is-numeric-negative.ts b/src/types/is-numeric-negative.ts new file mode 100644 index 00000000..863a01a8 --- /dev/null +++ b/src/types/is-numeric-negative.ts @@ -0,0 +1,51 @@ +/** + * @file Type Definitions - IsNegativeNumeric + * @module tutils/types/IsNegativeNumeric + */ + +import type IfAnyOrNever from './if-any-or-never' +import type NegativeNumeric from './numeric-negative' + +/** + * Returns a boolean indicating if `T` extends {@linkcode NegativeNumeric}. + * + * @example + * type X = IsNegativeNumeric<'-1'> + * // true + * @example + * type X = IsNegativeNumeric<-1 | -2 | '-1' | '-2' | '0' | '1' | 0 | 1> + * // boolean + * @example + * type X = IsNegativeNumeric<'0'> + * // false + * @example + * type X = IsNegativeNumeric<'1'> + * // false + * @example + * type X = IsNegativeNumeric<-1> + * // false + * @example + * type X = IsNegativeNumeric<1> + * // false + * @example + * type X = IsNegativeNumeric + * // false + * @example + * type X = IsNegativeNumeric + * // false + * @example + * type X = IsNegativeNumeric + * // false + * @example + * type X = IsNegativeNumeric + * // false + * + * @template T - Type to evaluate + */ +type IsNegativeNumeric = IfAnyOrNever< + T, + false, + T extends NegativeNumeric ? true : false +> + +export type { IsNegativeNumeric as default } diff --git a/src/types/is-numeric.ts b/src/types/is-numeric.ts index fa1568ca..45c88766 100644 --- a/src/types/is-numeric.ts +++ b/src/types/is-numeric.ts @@ -5,19 +5,56 @@ import type IfAnyOrNever from './if-any-or-never' import type Numeric from './numeric' +import type NegativeNumeric from './numeric-negative' import type Trim from './trim' /** - * Returns a boolean indicating if `T` is a {@linkcode Numeric}. + * Returns a boolean indicating if `T` extends {@linkcode NegativeNumeric} or + * {@linkcode Numeric}. * * @see https://github.com/microsoft/TypeScript/issues/46109 * + * @example + * type X = IsNumeric<'-1'> + * // true + * @example + * type X = IsNumeric<'0'> + * // true + * @example + * type X = IsNumeric<'1'> + * // true + * @example + * type X = IsNumeric<-1 | -2 | '-1' | '-2' | '0' | '1' | 0 | 1> + * // boolean + * @example + * type X = IsNumeric<1> + * // false + * @example + * type X = IsNumeric + * // false + * @example + * type X = IsNumeric + * // false + * @example + * type X = IsNumeric + * // false + * @example + * type X = IsNumeric + * // false + * @example + * type X = IsNumeric + * // false + * * @template T - Type to evaluate */ type IsNumeric = IfAnyOrNever< T, false, - [T] extends [Numeric] ? ([Trim] extends [T] ? true : false) : false + T extends NegativeNumeric | Numeric + ? Trim extends T + ? true + : false + : false > export type { IsNumeric as default } diff --git a/src/types/is-object-curly.ts b/src/types/is-object-curly.ts index dcdffd1a..137aa4b5 100644 --- a/src/types/is-object-curly.ts +++ b/src/types/is-object-curly.ts @@ -7,16 +7,36 @@ import type IfAnyOrNever from './if-any-or-never' import type ObjectCurly from './object-curly' /** - * Returns a boolean indicating if `T` is a curly-braced object. + * Returns a boolean indicating if `T` extends {@linkcode ObjectCurly}. * - * @see {@linkcode ObjectCurly} + * @example + * type X = IsObjectCurly<{ hello: 'world' }> + * // true + * @example + * type X = IsObjectCurly> + * // boolean + * @example + * type X = IsObjectCurly + * // false + * @example + * type X = IsObjectCurly + * // false + * @example + * type X = IsObjectCurly + * // false + * @example + * type X = IsObjectCurly + * // false + * @example + * type X = IsObjectCurly + * // false * * @template T - Type to evaluate */ type IsObjectCurly = IfAnyOrNever< T, false, - [T] extends [ObjectCurly] ? true : false + T extends ObjectCurly ? true : false > export type { IsObjectCurly as default } diff --git a/src/types/is-object-plain.ts b/src/types/is-object-plain.ts index 507641fa..890b5a64 100644 --- a/src/types/is-object-plain.ts +++ b/src/types/is-object-plain.ts @@ -7,16 +7,40 @@ import type IfAnyOrNever from './if-any-or-never' import type ObjectPlain from './object-plain' /** - * Returns a boolean indicating if `T` is a plain object. + * Returns a boolean indicating if `T` extends {@linkcode ObjectPlain}. * - * @see {@linkcode ObjectPlain} + * @example + * type X = IsObjectPlain<{ hello: 'world' }> + * // true + * @example + * interface Data { hello: 'world' } + * type X = IsObjectPlain> + * // true + * @example + * type X = IsObjectPlain> + * // boolean + * @example + * type X = IsObjectPlain + * // false + * @example + * type X = IsObjectPlain + * // false + * @example + * type X = IsObjectPlain + * // false + * @example + * type X = IsObjectPlain + * // false + * @example + * type X = IsObjectPlain + * // false * * @template T - Type to evaluate */ type IsObjectPlain = IfAnyOrNever< T, false, - [T] extends [ObjectPlain] ? true : false + T extends ObjectPlain ? true : false > export type { IsObjectPlain as default } diff --git a/src/types/is-object.ts b/src/types/is-object.ts index 49d7365f..5b541490 100644 --- a/src/types/is-object.ts +++ b/src/types/is-object.ts @@ -6,10 +6,35 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is an `object`. + * Returns a boolean indicating if `T` extends `object`. + * + * @example + * type X = IsObject<{ hello: 'world' }> + * // true + * @example + * type X = IsObject<(n: number) => string> + * // true + * @example + * type X = IsObject + * // true + * @example + * type X = IsObject + * // true + * @example + * type X = IsObject> + * // boolean + * @example + * type X = IsObject + * // false + * @example + * type X = IsObject + * // false + * @example + * type X = IsObject + * // false * * @template T - Type to evaluate */ -type IsObject = IfAnyOrNever +type IsObject = IfAnyOrNever export type { IsObject as default } diff --git a/src/types/is-primitive.ts b/src/types/is-primitive.ts index f6586ea8..5c86e206 100644 --- a/src/types/is-primitive.ts +++ b/src/types/is-primitive.ts @@ -7,14 +7,32 @@ import type IfAnyOrNever from './if-any-or-never' import type Primitive from './primitive' /** - * Returns a boolean indicating if `T` is a {@linkcode Primitive}. + * Returns a boolean indicating if `T` extends {@linkcode Primitive}. + * + * @example + * type X = IsPrimitive + * // true + * @example + * type X = IsPrimitive + * // true + * @example + * type X = IsPrimitive> + * // boolean + * @example + * type X = IsPrimitive + * // false + * @example + * type X = IsPrimitive + * // false + * @example + * type X = IsPrimitive + * // false + * @example + * type X = IsPrimitive + * // false * * @template T - Type to evaluate */ -type IsPrimitive = IfAnyOrNever< - T, - false, - [T] extends [Primitive] ? true : false -> +type IsPrimitive = IfAnyOrNever export type { IsPrimitive as default } diff --git a/src/types/is-string.ts b/src/types/is-string.ts index 3013f9ba..a70eab94 100644 --- a/src/types/is-string.ts +++ b/src/types/is-string.ts @@ -6,10 +6,29 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is a `string`. + * Returns a boolean indicating if `T` extends `string`. + * + * @example + * type X = IsString<'abc'> + * // true + * @example + * type X = IsString + * // true + * @example + * type X = IsString> + * // boolean + * @example + * type X = IsString + * // false + * @example + * type X = IsString + * // false + * @example + * type X = IsString + * // false * * @template T - Type to evaluate */ -type IsString = IfAnyOrNever +type IsString = IfAnyOrNever export type { IsString as default } diff --git a/src/types/is-symbol.ts b/src/types/is-symbol.ts index d5608ece..a7729763 100644 --- a/src/types/is-symbol.ts +++ b/src/types/is-symbol.ts @@ -6,10 +6,26 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is a `symbol`. + * Returns a boolean indicating if `T` extends `symbol`. + * + * @example + * type X = IsSymbol + * // true + * @example + * type X = IsSymbol> + * // boolean + * @example + * type X = IsSymbol + * // false + * @example + * type X = IsSymbol + * // false + * @example + * type X = IsSymbol + * // false * * @template T - Type to evaluate */ -type IsSymbol = IfAnyOrNever +type IsSymbol = IfAnyOrNever export type { IsSymbol as default } diff --git a/src/types/is-true.ts b/src/types/is-true.ts new file mode 100644 index 00000000..15adfa93 --- /dev/null +++ b/src/types/is-true.ts @@ -0,0 +1,34 @@ +/** + * @file Type Definitions - IsTrue + * @module tutils/types/IsTrue + */ + +import type IfAnyOrNever from './if-any-or-never' + +/** + * Returns a boolean indicating if `T` extends `true`. + * + * @example + * type X = IsTrue + * // true + * @example + * type X = IsTrue + * // boolean + * @example + * type X = IsTrue + * // false + * @example + * type X = IsTrue + * // false + * @example + * type X = IsTrue + * // false + * @example + * type X = IsTrue + * // false + * + * @template T - Type to evaluate + */ +type IsTrue = IfAnyOrNever + +export type { IsTrue as default } diff --git a/src/types/is-tuple.ts b/src/types/is-tuple.ts index 630a09d4..7f9d647b 100644 --- a/src/types/is-tuple.ts +++ b/src/types/is-tuple.ts @@ -4,18 +4,45 @@ */ import type IfAnyOrNever from './if-any-or-never' +import type IsLiteral from './is-literal' +import type Length from './length' /** - * Returns a boolean indicating if `T` is a [tuple][1]. + * Returns a boolean indicating if `T` extends a [tuple][1]. * * [1]: https://www.codecademy.com/resources/docs/typescript/tuples * + * @example + * type X = IsTuple + * // true + * @example + * type X = IsTuple + * // true + * @example + * type X = IsTuple> + * // boolean + * @example + * type X = IsTuple[]> + * // false + * @example + * type X = IsTuple + * // false + * @example + * type X = IsTuple + * // false + * @example + * type X = IsTuple + * // false + * @example + * type X = IsTuple + * // false + * * @template T - Type to evaluate */ type IsTuple = IfAnyOrNever< T, false, - [T] extends [[infer U, ...infer Rest]] ? true : false + T extends readonly unknown[] ? IsLiteral, number> : false > export type { IsTuple as default } diff --git a/src/types/is-undefined.ts b/src/types/is-undefined.ts index 15b9b898..342de568 100644 --- a/src/types/is-undefined.ts +++ b/src/types/is-undefined.ts @@ -6,14 +6,26 @@ import type IfAnyOrNever from './if-any-or-never' /** - * Returns a boolean indicating if `T` is `undefined`. + * Returns a boolean indicating if `T` extends `undefined`. + * + * @example + * type X = IsUndefined + * // true + * @example + * type X = IsUndefined + * // boolean + * @example + * type X = IsUndefined + * // false + * @example + * type X = IsUndefined + * // false + * @example + * type X = IsUndefined + * // false * * @template T - Type to evaluate */ -type IsUndefined = IfAnyOrNever< - T, - false, - [T] extends [undefined] ? true : false -> +type IsUndefined = IfAnyOrNever export type { IsUndefined as default } diff --git a/src/types/is-unknown.ts b/src/types/is-unknown.ts index c384bd2e..8ecbf549 100644 --- a/src/types/is-unknown.ts +++ b/src/types/is-unknown.ts @@ -8,6 +8,19 @@ import type IfAny from './if-any' /** * Returns a boolean indicating if `T` is `unknown`. * + * @example + * type X = IsUnknown + * // true + * @example + * type X = IsUnknown + * // false + * @example + * type X = IsUnknown + * // false + * @example + * type X = IsUnknown + * // false + * * @template T - Type to evaluate */ type IsUnknown = IfAny diff --git a/src/types/join.ts b/src/types/join.ts index a916c45d..4439e22b 100644 --- a/src/types/join.ts +++ b/src/types/join.ts @@ -4,32 +4,42 @@ */ import type EmptyArray from './empty-array' +import type EmptyString from './empty-string' import type Fallback from './fallback' import type Joinable from './joinable' +import type NIL from './nil' +import type Stringify from './stringify' /** - * Joins an array of {@linkcode Joinable} items using the given `Delimiter`. + * Converts array `T` to a single string delimited by `S`. * - * @template A - Array to evaluate - * @template Delimiter - String delimiter + * If `S` is omitted, items will be separated with a comma (`,`). + * + * @see {@linkcode Joinable} + * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/join + * + * @todo examples + * + * @template T - Array to join + * @template S - Array item separator */ type Join< - A extends readonly Joinable[], - Delimiter extends string = '.' -> = A extends EmptyArray - ? '' - : A extends readonly [Joinable?] - ? `${Fallback}` - : A extends readonly [ - infer Head extends Joinable, - ...infer Tail extends readonly Joinable[] + T extends readonly Joinable[], + S extends string = ',' +> = T extends Readonly + ? EmptyString + : T extends readonly [Joinable?] + ? Stringify> + : T extends readonly [ + infer H extends Joinable, + ...infer R extends readonly Joinable[] ] - ? `${Fallback}${Delimiter}${Join}` - : A extends readonly [ - ...infer First extends readonly Joinable[], - infer Last extends Joinable + ? `${Fallback}${S}${Join}` + : T extends readonly [ + ...infer R extends readonly Joinable[], + infer L extends Joinable ] - ? `${Join}${Delimiter}${Fallback}` + ? `${Join}${S}${Fallback}` : string export type { Join as default } diff --git a/src/types/keyof.ts b/src/types/keyof.ts new file mode 100644 index 00000000..96736827 --- /dev/null +++ b/src/types/keyof.ts @@ -0,0 +1,51 @@ +/** + * @file Type Definitions - Keyof + * @module tutils/types/Keyof + */ + +import type { tag as EmptyObjectTag } from './empty-object' +import type IfAny from './if-any' +import type IfNever from './if-never' +import type Indices from './indices' +import type IsTuple from './is-tuple' +import type Primitive from './primitive' +import type PropertyKey from './property-key' +import type Stringify from './stringify' + +/** + * Constructs a union of `T`'s property keys. + * + * @todo document index signature clobbering + * + * @todo examples + * + * @template T - Type to evaluate + * @template F - Key type filter + */ +type Keyof = Extract< + IfAny< + T, + keyof T, + IfNever< + T, + never, + T extends string | readonly unknown[] + ? Indices extends infer I extends number + ? + | Exclude< + keyof T, + number | (IsTuple extends true ? Stringify : never) + > + | I + : never + : T extends object + ? Exclude<{ [K in keyof T]: K }[keyof T], typeof EmptyObjectTag> + : T extends Primitive + ? keyof T + : never + > + >, + IfAny> +> + +export type { Keyof as default } diff --git a/src/types/keys-optional-exact.ts b/src/types/keys-optional-exact.ts new file mode 100644 index 00000000..31c6995e --- /dev/null +++ b/src/types/keys-optional-exact.ts @@ -0,0 +1,61 @@ +/** + * @file Type Definitions - ExactOptionalKeys + * @module tutils/types/ExactOptionalKeys + */ + +import type Get from './get' +import type IfAny from './if-any' +import type IfEqual from './if-equal' +import type OptionalKeys from './keys-optional' +import type PropertyKey from './property-key' + +/** + * Constructs a union of exact optional keys. + * + * @see https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes + * + * @example + * type X = ExactOptionalKeys<{ id: string; name?: string }> + * // 'name' + * @example + * type X = ExactOptionalKeys<{ id: string; name: string | undefined }> + * // never + * @example + * type X = ExactOptionalKeys<['a', 'b', 'c'?]> + * // -1 | 2 + * @example + * type X = ExactOptionalKeys<['a', 'b', 'c']> + * // -never + * @example + * type X = ExactOptionalKeys + * // 'id' + * @example + * type X = ExactOptionalKeys + * // never + * @example + * type X = ExactOptionalKeys<'abc'> + * // never + * @example + * type X = ExactOptionalKeys + * // never + * + * @template T - Type to evaluate + */ +type ExactOptionalKeys = IfAny< + T, + never, + T extends unknown + ? Extract< + OptionalKeys extends infer K extends keyof T + ? { + [H in K]: IfEqual, Get, H>, never, H> + } extends infer X + ? X[keyof X] + : never + : never, + PropertyKey + > + : never +> + +export type { ExactOptionalKeys as default } diff --git a/src/types/keys-optional.ts b/src/types/keys-optional.ts index 79d5ae8c..a41ad772 100644 --- a/src/types/keys-optional.ts +++ b/src/types/keys-optional.ts @@ -3,16 +3,89 @@ * @module tutils/types/OptionalKeys */ +import type IfAny from './if-any' +import type IfRequiredKey from './if-key-required' +import type IfTuple from './if-tuple' +import type Indices from './indices' +import type Length from './length' +import type Opaque from './opaque' +import type { tag as OpaqueTag } from './opaque' +import type Primitive from './primitive' +import type PropertyKey from './property-key' +import type Stringify from './stringify' +import type Subtract from './subtract' +import type UnwrapNumeric from './unwrap-numeric' + /** - * Extracts all optional keys from `T`. + * Constructs a union of optional keys. + * + * @example + * type X = OptionalKeys<{ id: string; name?: string }> + * // 'name' + * @example + * type X = OptionalKeys<{ id: string; name: string | undefined }> + * // never + * @example + * type X = OptionalKeys + * // -1 | 2 + * @example + * type X = OptionalKeys + * // -never + * @example + * type X = OptionalKeys<{ id: string }[]> + * // never + * @example + * type X = OptionalKeys<'abc'> + * // never + * @example + * type X = OptionalKeys + * // never + * @example + * type X = OptionalKeys + * // never + * @example + * type X = OptionalKeys + * // never + * @example + * type X = OptionalKeys + * // never * * @template T - Type to evaluate */ -type OptionalKeys = Exclude< - NonNullable extends infer U - ? { [K in keyof U]: U extends Record ? never : K }[keyof U] - : never, - undefined +type OptionalKeys = IfAny< + T, + never, + Extract< + T extends unknown + ? { + [H in keyof (T extends Opaque + ? T + : T extends Primitive + ? Omit, typeof OpaqueTag> + : T) as IfRequiredKey]: T extends readonly unknown[] + ? IfTuple< + T, + H extends Stringify> + ? UnwrapNumeric extends infer N extends number + ? + | N + | UnwrapNumeric< + Exclude< + `-${Subtract>, N>}`, + '-0' + > + > + : never + : H, + H + > + : H + } extends infer X + ? X[keyof X] + : never + : never, + PropertyKey + > > export type { OptionalKeys as default } diff --git a/src/types/keys-readonly.ts b/src/types/keys-readonly.ts new file mode 100644 index 00000000..418041e8 --- /dev/null +++ b/src/types/keys-readonly.ts @@ -0,0 +1,82 @@ +/** + * @file Type Definitions - ReadonlyKeys + * @module tutils/types/ReadonlyKeys + */ + +import type IfAny from './if-any' +import type IfEqual from './if-equal' +import type IfTuple from './if-tuple' +import type Indices from './indices' +import type Length from './length' +import type Opaque from './opaque' +import type { tag as OpaqueTag } from './opaque' +import type Primitive from './primitive' +import type PropertyKey from './property-key' +import type Stringify from './stringify' + +/** + * Constructs a union of readonly keys. + * + * @see https://github.com/microsoft/TypeScript/issues/14295 + * + * @example + * type X = ReadonlyKeys<{ readonly id: string; name: string }> + * // 'id' + * @example + * type X = ReadonlyKeys<{ id: string; name: string }> + * // never + * @example + * type X = ReadonlyKeys + * // typeof Symbol.unscopables | -1 | -2 | 'length' | 0 | 1 + * @example + * type X = ReadonlyKeys<'data'> + * // 'length' + * @example + * type X = ReadonlyKeys + * // never + * @example + * type X = ReadonlyKeys + * // never + * @example + * type X = ReadonlyKeys + * // never + * + * @template T - Type to evaluate + */ +type ReadonlyKeys = IfAny< + T, + never, + Extract< + T extends unknown + ? + | ({ + [H in keyof (T extends Opaque + ? T + : T extends Primitive + ? Omit, typeof OpaqueTag> + : T) as T extends readonly unknown[] + ? IfTuple> ? never : H, H> + : H]: { [K in H]: T[K & keyof T] } extends infer V + ? IfEqual< + V, + { readonly [Q in H]: T[Q & keyof T] }, + H, + IfEqual + > + : never + } extends infer X + ? X[keyof X] + : never) + | (T extends readonly unknown[] + ? number extends Length + ? never + : T extends { push: unknown[]['push'] } + ? never + : Indices + : never) + : never, + PropertyKey + > +> + +export type { ReadonlyKeys as default } diff --git a/src/types/keys-required.ts b/src/types/keys-required.ts index c55c857f..5df09678 100644 --- a/src/types/keys-required.ts +++ b/src/types/keys-required.ts @@ -3,16 +3,124 @@ * @module tutils/types/RequiredKeys */ +import type IfAny from './if-any' +import type IfEqual from './if-equal' +import type IfIndexSignature from './if-index-signature' +import type IfLiteral from './if-literal' +import type IfTuple from './if-tuple' +import type Indices from './indices' +import type Intersection from './intersection' +import type Length from './length' +import type Opaque from './opaque' +import type { tag as OpaqueTag } from './opaque' +import type Primitive from './primitive' +import type PropertyKey from './property-key' +import type Stringify from './stringify' +import type Subtract from './subtract' +import type UnwrapNumeric from './unwrap-numeric' + /** - * Extracts all required keys from `T`. + * Constructs a union of required keys. + * + * @todo document index signature clobbering + * + * @example + * type X = RequiredKeys<{ id: string; name?: string }> + * // 'id' + * @example + * type X = RequiredKeys<{ id: string; name: string | undefined }> + * // 'id' | 'name' + * @example + * type X = RequiredKeys + * // Exclude | -2 | 0 + * @example + * type X = RequiredKeys<'a'> + * // Exclude | 0 | 1 + * @example + * type X = RequiredKeys + * // keyof string[] + * @example + * type X = RequiredKeys + * // keyof string + * @example + * type X = RequiredKeys + * // never + * @example + * type X = RequiredKeys + * // never + * @example + * type X = RequiredKeys + * // never * * @template T - Type to evaluate */ -type RequiredKeys = Exclude< - NonNullable extends infer U - ? { [K in keyof U]: U extends Record ? K : never }[keyof U] - : never, - undefined +type RequiredKeys = IfAny< + T, + never, + Extract< + T extends unknown + ? { + [H in keyof (T extends Opaque + ? T + : T extends Primitive + ? Omit, typeof OpaqueTag> + : T) as IfIndexSignature< + T, + H, + IfTuple>, + H + >]: IfIndexSignature< + T, + H, + H, + IfEqual< + { [K in H]: T[K & keyof T] }, + { + [K in keyof Required< + T extends Opaque + ? T + : T extends Primitive + ? Omit, typeof OpaqueTag> + : T + > as Intersection]: Required[K & keyof T] + }, + T extends readonly unknown[] + ? IfTuple< + T, + H extends Stringify> + ? UnwrapNumeric extends infer N extends number + ? + | N + | UnwrapNumeric< + Exclude< + `-${Subtract>, N>}`, + '-0' + > + > + : never + : H, + H + > + : H, + T extends readonly unknown[] + ? H extends keyof unknown[] + ? H + : never + : never + > + > + } extends infer X + ? + | X[keyof X] + | (T extends string + ? number extends Length + ? never + : Indices + : never) + : never + : never, + PropertyKey + > > export type { RequiredKeys as default } diff --git a/src/types/keys.ts b/src/types/keys.ts deleted file mode 100644 index 9ce9ad50..00000000 --- a/src/types/keys.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file Type Definitions - Keys - * @module tutils/types/Keys - */ - -import type BuiltInObject from './built-in-object' -import type Class from './class' -import type EmptyArray from './empty-array' -import type EmptyObject from './empty-object' -import type Fn from './fn' -import type IfTuple from './if-tuple' -import type Indices from './indices' -import type Numeric from './numeric' -import type Primitive from './primitive' - -/** - * Constructs a union of enumerable string-keyed property names. - * - * **Note**: TypeScript does not track enumerability. This type does its best to - * remove non-enumerable properties for built-in objects only. - * - * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/keys - * @see https://github.com/microsoft/TypeScript/issues/9726 - * - * @template T - Type to evaluate - * @template B - Built-in objects - */ -type Keys = Extract< - NonNullable extends readonly unknown[] - ? NonNullable extends EmptyArray - ? Exclude, keyof any[]> | Indices - : IfTuple< - NonNullable, - Exclude, keyof any[]> | Indices, - Exclude, keyof any[]> | Numeric - > - : NonNullable extends Readonly - ? Exclude, keyof Fn> - : NonNullable extends Readonly> - ? T extends Readonly>> - ? Exclude, keyof Extract, U>> - : never - : T extends Readonly - ? T extends infer U - ? Exclude>> - : never - : T extends EmptyObject - ? never - : keyof NonNullable, - string -> - -export type { Keys as default } diff --git a/src/types/length.ts b/src/types/length.ts new file mode 100644 index 00000000..c84bac34 --- /dev/null +++ b/src/types/length.ts @@ -0,0 +1,48 @@ +/** + * @file Type Definitions - Length + * @module tutils/types/Length + */ + +import type EmptyString from './empty-string' +import type IfAny from './if-any' +import type Split from './split' + +/** + * Returns the length of `T`. + * + * @example + * type X = Length<['a', 'b', 'c']> + * // 3 + * @example + * type X = Length<['a', 'b', 'c'?]> + * // 2 | 3 + * @example + * type X = Length<'abc'> + * // 3 + * @example + * type X = Length> + * // 0 + * @example + * type X = Length + * // 0 + * @example + * type X = Length + * // number + * @example + * type X = Length + * // number + * @example + * type X = Length + * // number + * @example + * type X = Length + * // never + * + * @template T - Type to evaluate + */ +type Length = + T['length'] extends infer L extends number + ? IfAny['length'] : L> + : never + +export type { Length as default } diff --git a/src/types/merge-defaults.ts b/src/types/merge-defaults.ts deleted file mode 100644 index a63c0b96..00000000 --- a/src/types/merge-defaults.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file Type Definitions - MergeDefaults - * @module tutils/types/MergeDefaults - */ - -import type EmptyArray from './empty-array' -import type EmptyObject from './empty-object' -import type Head from './head' -import type IfNever from './if-never' -import type Merge from './merge' -import type ObjectCurly from './object-curly' -import type OneOrMany from './one-or-many' -import type Optional from './optional' - -/** - * Assigns properties from one or more source objects to target object `T` for - * all optional properties in `T`. - * - * Source objects are applied from left to right. Once a property is set on `T`, - * additional values of the same property are ignored if the property is no - * longer optional. - * - * @template T - Target object - * @template U - Source object or source object array - */ -type MergeDefaults< - T extends ObjectCurly, - U extends OneOrMany> = EmptyObject -> = U extends EmptyArray | EmptyObject - ? T - : U extends Partial - ? MergeDefaults - : U extends [infer H, ...infer Rest extends readonly ObjectCurly[]] - ? Merge< - T, - { - [K in keyof H & keyof T]: T[K & keyof T] extends infer V - ? IfNever< - Extract, - V, - Exclude> | H[K & keyof H] - > - : never - } - > extends infer V extends ObjectCurly - ? Rest extends readonly Partial[] - ? MergeDefaults - : never - : never - : Head extends infer S extends Partial - ? MergeDefaults - : never - -export type { MergeDefaults as default } diff --git a/src/types/merge.ts b/src/types/merge.ts index 9fcd0482..4e365f30 100644 --- a/src/types/merge.ts +++ b/src/types/merge.ts @@ -4,20 +4,139 @@ */ import type EmptyObject from './empty-object' +import type Get from './get' +import type HasKeys from './has-keys' +import type IfOptionalKey from './if-key-optional' +import type IfExactOptionalKey from './if-key-optional-exact' +import type IfTrue from './if-true' +import type IfUndefined from './if-undefined' +import type IsAny from './is-any' +import type IsEqual from './is-equal' +import type IsNever from './is-never' +import type IsUndefined from './is-undefined' +import type NIL from './nil' +import type Nilable from './nilable' import type ObjectCurly from './object-curly' +import type ObjectPlain from './object-plain' +import type OneOrMany from './one-or-many' +import type PropertyKey from './property-key' import type Simplify from './simplify' /** - * Merges two types into one. + * Merges `T` and `H`. * - * Keys of `U` override `T`. + * @internal * * @template T - Target object - * @template U - Source object + * @template H - Source object + * @template M - Merge options + */ +type Merger< + T extends Nilable, + H, + M extends EmptyObject & { concat?: true; defaults?: true } = EmptyObject +> = IsAny extends true + ? Simplify & T> + : IsNever extends true + ? T + : H extends unknown + ? HasKeys extends true + ? IsEqual extends true + ? T + : Get extends infer C + ? Get extends infer D + ? { + [K in keyof ({ + [K in keyof H as IsUndefined extends true + ? K extends keyof T + ? never + : K + : K extends keyof T + ? IfTrue, K> + : K]: K + } & { + [K in keyof T as K extends keyof H + ? IsUndefined extends true + ? K + : IfTrue, never> + : K]: K + })]: Get< + T, + K, + IfExactOptionalKey + > extends infer V + ? K extends keyof H + ? Get< + H, + K, + IfExactOptionalKey + > extends infer W + ? [NonNullable, NonNullable] extends [ + infer A extends ObjectPlain, + infer B extends ObjectPlain + ] + ? Extract | Merger + : [NonNullable, NonNullable] extends [ + infer A extends readonly unknown[], + infer B extends readonly unknown[] + ] + ? Extract | IfTrue + : [D, K] extends [true, keyof T] + ? IfOptionalKey + : IfUndefined + : never + : K extends keyof T + ? V + : never + : never + } + : never + : never + : T + : T + +/** + * Merges `T` and one or more source objects. + * + * Plain objects are merged recursively. Arrays are overridden by default, but + * can be concatted recursively. Other types are overridden by assignment. + * Source properties that resolve to `undefined` are skipped if a target object + * value exists. + * + * If only defaults are to be merged, properties from one or more source objects + * will be assigned to target object `T` for all optional properties in `T`. + * + * An optional property is one where {@linkcode IfOptionalKey} yields `true`, or + * a property that only exists on a source object. + * + * Source objects are applied from left to right. Subsequent sources overwrite + * property assignments of previous sources. When merging defaults, subsequent + * source properties are ignored if the target property is no longer optional. + * + * @see {@linkcode ObjectPlain} + * + * @todo examples + * + * @template T - Target object + * @template U - Source object or source object array + * @template M - Merge options */ type Merge< - T extends ObjectCurly, - U extends ObjectCurly = EmptyObject -> = U extends EmptyObject ? T : Simplify & U> + T extends Nilable, + U extends OneOrMany = EmptyObject, + M extends EmptyObject & { concat?: true; defaults?: true } = EmptyObject +> = T extends ObjectCurly + ? IsAny extends true + ? Simplify & T> + : IsNever extends true + ? T + : U extends ObjectCurly + ? Merger + : U extends readonly ObjectCurly[] + ? U extends readonly [infer H, ...infer R extends ObjectCurly[]] + ? Merge, R, M> + : Merger + : never + : never export type { Merge as default } diff --git a/src/types/nilable.ts b/src/types/nilable.ts index 5aadf2db..1e30a18f 100644 --- a/src/types/nilable.ts +++ b/src/types/nilable.ts @@ -6,9 +6,13 @@ import type NIL from './nil' /** - * Constructs a union type of {@linkcode NIL} and `T`. + * Constructs a union of {@linkcode NIL} and `T`. * - * @template T - Value type + * @example + * type X = Nilable + * // string | null | undefined + * + * @template T - Type to evaluate */ type Nilable = NIL | T diff --git a/src/types/nullable.ts b/src/types/nullable.ts index c6809b2c..f3c7411e 100644 --- a/src/types/nullable.ts +++ b/src/types/nullable.ts @@ -4,9 +4,13 @@ */ /** - * Constructs a union type of `T` and `null`. + * Constructs a union of `T` and `null`. * - * @template T - Value type + * @example + * type X = Nullable + * // string | null + * + * @template T - Type to evaluate */ type Nullable = T | null diff --git a/src/types/number-like.ts b/src/types/number-like.ts index 9324b109..994493e9 100644 --- a/src/types/number-like.ts +++ b/src/types/number-like.ts @@ -4,10 +4,14 @@ */ import type Numeric from './numeric' +import type NegativeNumeric from './numeric-negative' /** - * A string that contains only numbers or a number. + * A number, or a string that contains only numbers. + * + * @see {@linkcode NegativeNumeric} + * @see {@linkcode Numeric} */ -type NumberLike = Numeric | number +type NumberLike = NegativeNumeric | Numeric | number export type { NumberLike as default } diff --git a/src/types/numeric-negative.ts b/src/types/numeric-negative.ts new file mode 100644 index 00000000..e9bd9b60 --- /dev/null +++ b/src/types/numeric-negative.ts @@ -0,0 +1,17 @@ +/** + * @file Type Definitions - NegativeNumeric + * @module tutils/types/NegativeNumeric + */ + +/** + * A negative numeric. + * + * A numeric is a string containing only numbers (not including the leading `-` + * if the numeric is negative). + * + * @example + * type X = '-1' + */ +type NegativeNumeric = `-${number}` + +export type { NegativeNumeric as default } diff --git a/src/types/numeric.ts b/src/types/numeric.ts index 4da3c1b7..c2fba0e2 100644 --- a/src/types/numeric.ts +++ b/src/types/numeric.ts @@ -6,8 +6,14 @@ import type Stringify from './stringify' /** - * A string that contains only numbers (not including the leading `-` if the - * numeric is negative). + * A string containing only numbers (not including the leading `-` if negative). + * + * @example + * '-1' + * @example + * '0' + * @example + * '1' */ type Numeric = Stringify diff --git a/src/types/object-curly.ts b/src/types/object-curly.ts index 335b30c2..b5569060 100644 --- a/src/types/object-curly.ts +++ b/src/types/object-curly.ts @@ -12,11 +12,14 @@ import type Simplify from './simplify' * Curly-braced objects are `object` types that are **not** arrays or functions * (e.g. instance objects, pojos). * - * **Note**: Curly-braced object types **cannot** have `call` or `concat` - * properties. + * **Note**: Curly-braced object types **cannot** have a `Symbol.unscopables` or + * `arguments` property. */ type ObjectCurly = Simplify< - { [K in PropertyKey]?: any } & { call?: never; concat?: never } + { + [Symbol.unscopables]?: never + arguments?: never + } & { [K in PropertyKey]?: any } > export type { ObjectCurly as default } diff --git a/src/types/omit-index-signature.ts b/src/types/omit-index-signature.ts new file mode 100644 index 00000000..0a231b3a --- /dev/null +++ b/src/types/omit-index-signature.ts @@ -0,0 +1,30 @@ +/** + * @file Type Definitions - OmitIndexSignature + * @module tutils/types/OmitIndexSignature + */ + +import type IfIndexSignature from './if-index-signature' + +/** + * Removes index signatures from `T`, leaving only explicity defined keys. + * + * @example + * type X = OmitIndexSignature<{ + * [x: number]: any + * [x: string]: any + * [x: symbol]: any + * [x: `${bigint}`]: string + * [x: `${number}`]: string + * [x: `data.${string}`]: string + * hello: 'world' + * foo?: 'bar' + * }> + * // { hello: 'world'; foo?: 'bar' } + * + * @template T - Type to evaluate + */ +type OmitIndexSignature = T extends unknown + ? { [K in keyof T as IfIndexSignature]: T[K] } + : never + +export type { OmitIndexSignature as default } diff --git a/src/types/omit-native.ts b/src/types/omit-native.ts deleted file mode 100644 index 3863816d..00000000 --- a/src/types/omit-native.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file Type Definitions - OmitNative - * @module tutils/types/OmitNative - */ - -/** - * From `T`, omit a set of properties whose keys are in the union `K`. - * - * @see https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys - * - * @template T - Type to evaluate - * @template K - Keys to remove - */ -type OmitNative = Omit - -export type { OmitNative as default } diff --git a/src/types/omit.ts b/src/types/omit.ts deleted file mode 100644 index 2520e964..00000000 --- a/src/types/omit.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file Type Definitions - Omit - * @module tutils/types/Omit - */ - -import type EmptyObject from './empty-object' -import type Get from './get' -import type Head from './head' -import type IfExactOptionalKey from './if-key-optional-exact' -import type IfRequiredKey from './if-key-required' -import type IfNever from './if-never' -import type NumberString from './number-string' -import type Pick from './pick' -import type Optional from './optional' -import type Simplify from './simplify' -import type Stringify from './stringify' -import type UnionToIntersection from './union-to-intersection' - -/** - * From `T`, omit a set of properties whose keys are in the union `K`. - * - * This is the opposite of {@linkcode Pick}. - * - * Supports dot-notation for nested paths and array indexing. - * - * @template T - Type to evaluate - * @template K - Keys to remove - */ -type Omit = Simplify< - UnionToIntersection< - Head> extends infer H extends string - ? Pick> extends infer U - ? K extends `${infer F}.${infer Rest}` - ? Omit>, Rest> extends infer V - ? IfRequiredKey< - T, - F, - U & { [key in F]: V }, - IfExactOptionalKey< - T, - F, - U & { [key in F]?: V }, - U & { [key in F]?: Optional } - > - > - : never - : IfNever - : never - : never - > -> - -export type { Omit as default } diff --git a/src/types/one-or-many.ts b/src/types/one-or-many.ts index bed607a4..36aa539c 100644 --- a/src/types/one-or-many.ts +++ b/src/types/one-or-many.ts @@ -6,8 +6,12 @@ /** * Constructs a union of `T` and an array of `T` values. * + * @example + * type X = OneOrMany + * // string | readonly string[] + * * @template T - Type to evaluate */ -type OneOrMany = T | T[] | readonly T[] +type OneOrMany = T | readonly T[] export type { OneOrMany as default } diff --git a/src/types/opaque.ts b/src/types/opaque.ts index f7c50ede..e8ab406b 100644 --- a/src/types/opaque.ts +++ b/src/types/opaque.ts @@ -23,6 +23,8 @@ export declare const tag: unique symbol * @see https://github.com/Microsoft/TypeScript/issues/15408 * @see https://github.com/Microsoft/TypeScript/issues/15807 * + * @todo examples + * * @template B - Base type * @template T - Type token */ diff --git a/src/types/optional.ts b/src/types/optional.ts index 8aa7dc32..f9c79ee0 100644 --- a/src/types/optional.ts +++ b/src/types/optional.ts @@ -6,6 +6,10 @@ /** * Constructs a union of `T` and `undefined`. * + * @example + * type X = Optional + * // string | undefined + * * @template T - Type to evaluate */ type Optional = T | undefined diff --git a/src/types/or-lowercase.ts b/src/types/or-lowercase.ts index feb726d8..3e47fb8a 100644 --- a/src/types/or-lowercase.ts +++ b/src/types/or-lowercase.ts @@ -4,11 +4,18 @@ */ /** - * Adds lowercase alternatives to `T`. + * Constructs a union of `T` and its lowercase alternatives. * * @see https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html#lowercasestringtype * - * @template T - String type to evaluate + * @example + * type X = OrLowercase<'AND'> + * // 'AND' | 'and' + * @example + * type X = OrLowercase<'CRLF' | 'LF'> + * // 'CRLF' | 'LF' | 'crlf' | 'lf' + * + * @template T - Type to evaluate */ type OrLowercase = Lowercase | T diff --git a/src/types/or-uppercase.ts b/src/types/or-uppercase.ts index 3dcbb33f..d61fc325 100644 --- a/src/types/or-uppercase.ts +++ b/src/types/or-uppercase.ts @@ -4,11 +4,18 @@ */ /** - * Adds uppercase alternatives to `T`. + * Constructs a union of `T` and its uppercase alternatives. * * @see https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html#uppercasestringtype * - * @template T - String type to evaluate + * @example + * type X = OrUppercase<'and'> + * // 'AND' | 'and' + * @example + * type X = OrUppercase<'crlf' | 'lf'> + * // 'CRLF' | 'LF' | 'crlf' | 'lf' + * + * @template T - Type to evaluate */ type OrUppercase = T | Uppercase diff --git a/src/types/overwrite.ts b/src/types/overwrite.ts index e7825bd1..443068ee 100644 --- a/src/types/overwrite.ts +++ b/src/types/overwrite.ts @@ -3,16 +3,75 @@ * @module tutils/types/Overwrite */ -import type Simplify from './simplify' +import type EmptyObject from './empty-object' +import type HasKeys from './has-keys' +import type IsAnyOrNever from './is-any-or-never' +import type IsEqual from './is-equal' +import type ObjectCurly from './object-curly' +import type OmitIndexSignature from './omit-index-signature' +import type OneOrMany from './one-or-many' /** - * Replaces existing properties in `T` with properties in `U`. + * Assigns mutual properties of `H` to `T`. * - * @template T - Base type - * @template U - Type containing replacement values + * @internal + * + * @template T - Target object + * @template H - Source object + */ +type Overwriter = IsAnyOrNever extends true + ? T + : HasKeys extends true + ? IsEqual extends true + ? T + : H extends unknown + ? { + [K in keyof ({ + [K in keyof H as K extends keyof T ? K : never]: K + } & { + [K in keyof T as K extends keyof OmitIndexSignature ? never : K]: K + })]: K extends keyof OmitIndexSignature + ? K extends keyof H + ? H[K] + : never + : K extends keyof OmitIndexSignature + ? T[K] + : K extends keyof H + ? H[K] + : K extends keyof T + ? T[K] + : never + } + : T + : T + +/** + * Assigns properties from one or more source objects to target object `T` for + * all mutual properties in `T`. + * + * A mutual property is a property that is contained in both target object `T` + * and a source object. + * + * Source objects are applied from left to right. + * + * @todo examples + * + * @template T - Target object + * @template U - Source object or source object array */ -type Overwrite = Simplify<{ - [K in keyof T]: K extends keyof U ? U[K] : T[K] -}> +type Overwrite< + T extends ObjectCurly, + U extends OneOrMany = EmptyObject +> = T extends ObjectCurly + ? IsAnyOrNever extends true + ? T + : U extends ObjectCurly + ? Overwriter + : U extends readonly ObjectCurly[] + ? U extends readonly [infer H, ...infer R extends ObjectCurly[]] + ? Overwrite, R> + : Overwriter + : never + : never export type { Overwrite as default } diff --git a/src/types/partial-native.ts b/src/types/partial-native.ts new file mode 100644 index 00000000..65101c3b --- /dev/null +++ b/src/types/partial-native.ts @@ -0,0 +1,17 @@ +/** + * @file Type Definitions - PartialNative + * @module tutils/types/PartialNative + */ + +/** + * Constructs a type with all properties of `T` set to optional. + * + * This utility will return a type that represents all subsets of a given type. + * + * @see https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype + * + * @template T - Type to evaluate + */ +type PartialNative = Partial + +export type { PartialNative as default } diff --git a/src/types/partial.ts b/src/types/partial.ts new file mode 100644 index 00000000..259df9fe --- /dev/null +++ b/src/types/partial.ts @@ -0,0 +1,40 @@ +/** + * @file Type Definitions - Partial + * @module tutils/types/Partial + */ + +import type Fn from './fn' +import type NumberLike from './number-like' +import type Primitive from './primitive' +import type Simplify from './simplify' + +/** + * Constructs a type with all properties of `T` set to optional. + * + * @todo implement optional array item recursion + * @todo examples + * + * @template T - Type to evaluate + */ +type Partial = T extends unknown + ? { + [K in keyof T]?: T[K] extends infer V + ? V extends Readonly + ? Fn extends V + ? V + : Partial + : V extends Primitive + ? Primitive extends V + ? V + : Partial + : T extends readonly unknown[] + ? // preserve PartialNative functionality for plain arrays + K extends NumberLike | keyof unknown[] + ? V + : Simplify> + : Simplify> + : never + } + : never + +export type { Partial as default } diff --git a/src/types/path.ts b/src/types/path.ts index b92697f6..c8bc9f24 100644 --- a/src/types/path.ts +++ b/src/types/path.ts @@ -3,70 +3,126 @@ * @module tutils/types/Path */ -import type EmptyArray from './empty-array' -import type EmptyObject from './empty-object' -import type EmptyString from './empty-string' +import type At from './at' +import type BuiltIn from './built-in' +import type Dot from './dot' import type Fn from './fn' import type Get from './get' -import type Head from './head' +import type IfAny from './if-any' +import type IfNegative from './if-negative' +import type IfNumber from './if-number' +import type IfSymbol from './if-symbol' import type Indices from './indices' -import type Keys from './keys' +import type Join from './join' +import type Keyof from './keyof' +import type Length from './length' +import type Nilable from './nilable' import type NumberString from './number-string' -import type ObjectCurly from './object-curly' +import type Opaque from './opaque' import type Primitive from './primitive' import type PropertyKey from './property-key' +import type Stringify from './stringify' +import type Subtract from './subtract' +import type UnwrapNumeric from './unwrap-numeric' /** * Constructs a union of nested and top-level property paths. * - * Non-enumerable paths are not included for builtin objects and primitives. + * Nested and array-indexable paths are expressed using dot notation. * - * Nested and array-indexable paths will be expressed using dot notation. + * Non-enumerable property paths can be filtered out by setting `E` to `true`. * - * @see {@linkcode Keys} + * **Note**: TypeScript does not track enumerability. Non-enumerable properties + * are removed for {@linkcode BuiltIn} types only. + * + * @todo examples + * + * @see https://github.com/microsoft/TypeScript/issues/9726 * * @template T - Type to evaluate - * @template K - Keys to begin building union + * @template E - Enumerable property paths only */ -type Path> = T extends - | EmptyArray - | EmptyObject - | EmptyString - ? never - : Extract< - K extends NumberString - ? NonNullable extends readonly unknown[] - ? NonNullable extends infer U - ? Head> extends infer R - ? - | Indices> - | Path, keyof any[]>> - | `${Indices>}.${Path}` - : never - : never - : NonNullable> extends readonly unknown[] - ? NonNullable> extends infer U - ? Head>> extends infer R - ? - | K - | `${K}.${Indices>}.${Path}` - | `${K}.${Indices>}` - | `${K}.${Path, keyof any[]>>}` - : never - : never - : NonNullable> extends Readonly - ? NonNullable> extends infer U - ? K | `${K}.${Path, keyof Fn>>}` - : never - : NonNullable> extends ObjectCurly - ? NonNullable> extends infer U - ? K | `${K}.${Path>}` +type Path = false> = Extract< + IfAny< + T, + keyof T, + T extends unknown + ? { + [H in keyof (T extends string + ? number extends Length + ? Opaque + : T & { + [N in Stringify> as IfNegative]: At< + T, + N + > + } + : T extends Primitive + ? Opaque + : T) as IfSymbol< + H, + never, + E extends true + ? T extends string | readonly unknown[] + ? IfNumber< + H, + H, + H extends Keyof ? never : H + > + : T extends Readonly> + ? H extends Keyof> + ? never + : H + : H + : H + > extends infer H extends PropertyKey + ? T extends string | readonly unknown[] + ? number extends Length + ? H + : number extends H + ? never + : H + : H + : never]: ( + T extends string | readonly unknown[] + ? number extends Length + ? H + : H extends Stringify> + ? UnwrapNumeric extends infer N extends number + ? UnwrapNumeric< + Exclude<`-${Subtract>, N>}`, '-0'> + > extends infer M extends number + ? M | N + : never + : never + : H + : H + ) extends infer H extends PropertyKey + ? + | H + | Stringify + | (Get extends infer V + ? [H, NonNullable] extends [number, string] + ? T extends readonly unknown[] + ? Path extends infer P extends NumberString + ? Join<[Stringify, P], Dot> + : never + : never + : Readonly extends V + ? never + : Path extends infer P extends NumberString + ? Join<[Stringify, P], Dot> + : never + : never) : never - : Get extends Readonly - ? K - : never - : never, - string - > + } extends infer X + ? X[keyof X] + : never + : never + >, + NumberString +> extends infer P extends NumberString + ? P + : never export type { Path as default } diff --git a/src/types/pick-native.ts b/src/types/pick-native.ts deleted file mode 100644 index 4f37e45b..00000000 --- a/src/types/pick-native.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file Type Definitions - PickNative - * @module tutils/types/PickNative - */ - -/** - * From `T`, pick a set of properties whose keys are in the union `K`. - * - * @see https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys - * - * @template T - Type to evaluate - * @template K - Keys to select - */ -type PickNative = Pick - -export type { PickNative as default } diff --git a/src/types/pick.ts b/src/types/pick.ts deleted file mode 100644 index 4bf9116b..00000000 --- a/src/types/pick.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @file Type Definitions - Pick - * @module tutils/types/Pick - */ - -import type At from './at' -import type EnsureString from './ensure-string' -import type Get from './get' -import type Head from './head' -import type IfOptionalKey from './if-key-optional' -import type IfExactOptionalKey from './if-key-optional-exact' -import type IfRequiredKey from './if-key-required' -import type IfNever from './if-never' -import type IfTuple from './if-tuple' -import type IfUndefined from './if-undefined' -import type Join from './join' -import type NIL from './nil' -import type NumberString from './number-string' -import type Numeric from './numeric' -import type Simplify from './simplify' -import type Tail from './tail' - -/** - * From `T`, pick a set of properties whose keys are in the union `K`. - * - * Supports dot-notation for nested paths and array indexing. - * - * @template T - Type to evaluate - * @template K - Keys to select - */ -type Pick = Simplify< - { - [H in Head<`${K}`>]: Get extends infer U - ? Join<[H, NumberString]> extends infer J - ? Extract extends infer X - ? IfNever< - X, - U, - Tail extends infer P - ? NonNullable extends readonly unknown[] - ? Head

extends infer HNext - ? HNext extends Numeric | number - ? At, HNext> extends infer Item - ? IfUndefined< - Item, - [Item], - Pick< - NonNullable, - EnsureString> - > extends infer Next - ? IfTuple, [Next], Next[]> - : never - > - : never - : Pick, EnsureString

> - : never - : Pick, EnsureString

> - : never - > - : never - : never - : never - } extends infer R - ? NonNullable extends readonly unknown[] - ? Extract | R - : { - [K in keyof R as IfExactOptionalKey]?: Exclude< - R[K], - undefined - > - } & { - [K in keyof R as IfOptionalKey< - T, - K, - IfExactOptionalKey, - never - >]?: R[K] - } & { - [K in keyof R as IfRequiredKey]: R[K] - } - : never -> - -export type { Pick as default } diff --git a/src/types/promisable.ts b/src/types/promisable.ts index 2c0e57b1..326f4ecb 100644 --- a/src/types/promisable.ts +++ b/src/types/promisable.ts @@ -8,6 +8,10 @@ * * @see https://github.com/microsoft/TypeScript/issues/31394 * + * @example + * type X = Promisable + * // PromiseLike | string[] + * * @template T - Type to evalute */ type Promisable = PromiseLike | T diff --git a/src/types/range-natural.ts b/src/types/range-natural.ts index 502e0556..715639c7 100644 --- a/src/types/range-natural.ts +++ b/src/types/range-natural.ts @@ -6,16 +6,37 @@ import type EmptyArray from './empty-array' /** - * Constructs a union of natural numbers in the range `[Acc['length'],Max)`. + * Construct a union of natural numbers in the range `[Acc['length'],M)`. * - * @template Max - Upper bound of range (exclusive) + * @example + * type X = NaturalRange<10> + * // 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 + * @example + * type X = NaturalRange<10, [never, never, never, never, never]> + * // 5 | 6 | 7 | 8 | 9 + * @example + * type X = NaturalRange<1> + * // 0 + * @example + * type X = NaturalRange<0> + * // never + * @example + * type X = NaturalRange + * // never + * @example + * type X = NaturalRange + * // never + * + * @template M - Upper bound of range (exclusive) * @template Acc - Range accumulator */ type NaturalRange< - Max extends number, + M extends number, Acc extends readonly number[] = EmptyArray -> = Acc['length'] extends Max - ? Acc[number] - : NaturalRange +> = M extends number + ? Acc['length'] extends M + ? Acc[number] + : NaturalRange + : never export type { NaturalRange as default } diff --git a/src/types/reverse.ts b/src/types/reverse.ts new file mode 100644 index 00000000..c087d142 --- /dev/null +++ b/src/types/reverse.ts @@ -0,0 +1,86 @@ +/** + * @file Type Definitions - Reverse + * @module tutils/types/Reverse + */ + +import type EmptyArray from './empty-array' +import type EmptyString from './empty-string' +import type IfAny from './if-any' +import type Join from './join' +import type Length from './length' +import type Split from './split' + +/** + * Reverses array `T`. + * + * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse + * + * @internal + * + * @template T - Array to reverse + * @template Acc - Reversal accumulator + */ +type ReverseArray< + T extends readonly unknown[], + Acc extends readonly unknown[] = EmptyArray +> = number extends Length + ? T + : T extends Readonly + ? Acc + : T extends readonly [...infer X extends readonly T[number][], infer H] + ? ReverseArray + : T extends readonly [...infer X extends readonly T[number][], (infer H)?] + ? ReverseArray + : never + +/** + * Reverses `T`. + * + * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse + * + * @example + * type X = Reverse<['a', 'b', 'c']> + * // ['c', 'b', 'a'] + * @example + * type X = Reverse<['a', 'b', 'c'?]> + * // ['c' | undefined, 'b', 'a'] + * @example + * type X = Reverse<['a']> + * // ['a'] + * @example + * type X = Reverse<['c'?]> + * // ['c'?] + * @example + * type X = Reverse<'abc'> + * // 'cba' + * @example + * type X = Reverse<'racecar'> + * // 'racecar' + * @example + * type X = Reverse + * // string[] + * @example + * type X = Reverse + * // string + * @example + * type X = Reverse + * // any[] + * @example + * type X = Reverse + * // never + * + * @template T - Array or string to reverse + */ +type Reverse = IfAny< + T, + any[], + Length> extends 1 + ? T + : T extends string + ? Join>, EmptyString> + : T extends readonly unknown[] + ? ReverseArray + : never +> + +export type { Reverse as default } diff --git a/src/types/sift.ts b/src/types/sift.ts index e28a5ff5..ab93e325 100644 --- a/src/types/sift.ts +++ b/src/types/sift.ts @@ -5,23 +5,42 @@ import type EmptyArray from './empty-array' import type Falsy from './falsy' -import type IfNever from './if-never' +import type IfAnyOrNever from './if-any-or-never' +import type IfEqual from './if-equal' import type IfTuple from './if-tuple' import type UnionToTuple from './union-to-tuple' /** - * Filters falsy types out of `T`. + * Removes {@linkcode Falsy} values from `T`. * - * @see {@linkcode Falsy} + * @example + * type X = Sift + * // [] + * @example + * type X = Sift, 0, boolean]> + * // [string, true] + * @example + * type X = Sift[]> + * // string[] + * @example + * type X = Sift + * // [] + * @example + * type X = Sift + * // [] * * @template T - Array to filter */ -type Sift = T extends EmptyArray - ? T - : T extends readonly (infer I)[] - ? Exclude extends infer W - ? IfTuple, IfNever, W[]>> +type Sift = IfAnyOrNever< + T, + EmptyArray, + T extends Readonly + ? T + : T extends readonly (infer I)[] + ? Exclude extends infer V + ? IfEqual, V[]>> + : never : never - : never +> export type { Sift as default } diff --git a/src/types/simplify.ts b/src/types/simplify.ts index fd875537..83f9fdd8 100644 --- a/src/types/simplify.ts +++ b/src/types/simplify.ts @@ -12,6 +12,6 @@ * * @template T - Type to evaluate */ -type Simplify = { [K in keyof T]: T[K] } & {} +type Simplify = T extends unknown ? { [K in keyof T]: T[K] } : never export type { Simplify as default } diff --git a/src/types/split.ts b/src/types/split.ts index cd4998ae..cea17fc5 100644 --- a/src/types/split.ts +++ b/src/types/split.ts @@ -5,28 +5,66 @@ import type EmptyArray from './empty-array' import type EmptyString from './empty-string' -import type EnsureString from './ensure-string' +import type IfAny from './if-any' +import type IfNever from './if-never' import type Optional from './optional' +import type Stringify from './stringify' /** - * Splits string `S` using the given `Delimiter`. + * Converts string `T` to an array of substrings. + * + * @todo examples * * @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/split * * @template T - String to split - * @template Delimiter - String delimiter + * @template S - String separator + * @template Acc - Substring accumulator */ type Split< T extends string, - Delimiter extends Optional = undefined -> = RegExp extends Delimiter - ? T extends EmptyString - ? EmptyArray - : string[] - : T extends `${infer Head}${EnsureString}${infer Tail}` - ? [Head, ...Split] - : T extends Delimiter - ? EmptyArray - : [T] + S extends Optional = undefined, + Acc extends readonly string[] = EmptyArray +> = IfAny< + T, + string[], + IfNever< + T, + EmptyArray, + T extends object + ? S extends undefined + ? [T] + : string[] + : T extends `${infer H}${Stringify}${infer R}` + ? Split + : [...Acc, T] extends infer R + ? T extends EmptyString + ? S extends EmptyString + ? Acc + : R + : Capitalize extends T + ? S extends undefined + ? R + : string[] + : Lowercase extends T + ? S extends undefined + ? R + : Lowercase[] + : Uncapitalize extends T + ? S extends undefined + ? R + : string[] + : Uppercase extends T + ? S extends undefined + ? R + : Uppercase[] + : S extends RegExp + ? string[] + : string extends S + ? string[] + : R + : Acc + > +> export type { Split as default } diff --git a/src/types/stringify.ts b/src/types/stringify.ts index 7617ac9d..20a14fb1 100644 --- a/src/types/stringify.ts +++ b/src/types/stringify.ts @@ -8,6 +8,31 @@ import type Joinable from './joinable' /** * Converts `T` to a string if `T` extends {@linkcode Joinable}. * + * @example + * type X = Stringify<1> + * // '1' + * @example + * type X = Stringify + * // 'false' | 'true' + * @example + * type X = Stringify<'y'> + * // 'y' + * @example + * type X = Stringify + * // string + * @example + * type X = Stringify<{ hello: 'world' }> + * // never + * @example + * type X = Stringify + * // `${any}` + * @example + * type X = Stringify + * // never + * @example + * type X = Stringify + * // never + * * @template T - Type to evaluate */ type Stringify = T extends Joinable ? `${T}` : never diff --git a/src/types/subtract.ts b/src/types/subtract.ts new file mode 100644 index 00000000..8d84f6cf --- /dev/null +++ b/src/types/subtract.ts @@ -0,0 +1,50 @@ +/** + * @file Type Definitions - Subtract + * @module tutils/types/Subtract + */ + +import type EmptyArray from './empty-array' + +/** + * Creates a tuple of length `L`. + * + * @internal + * + * @template L - Tuple size + * @template Acc - Tuple elements accumulator + */ +type BuildTuple< + L extends number, + Acc extends readonly unknown[] = EmptyArray +> = Acc extends { length: L } ? Acc : BuildTuple + +/** + * Returns the difference between `M` and `S`. + * + * @internal + * + * @template M - Minuend + * @template S - Subtrahend + */ +type Calculate = BuildTuple extends [ + ...infer U, + ...BuildTuple +] + ? U['length'] + : never + +/** + * Returns the difference between `M` and `S`. + * + * @internal + * + * @template M - Minuend + * @template S - Subtrahend + */ +type Subtract = M extends number + ? S extends number + ? Calculate + : never + : never + +export type { Subtract as default } diff --git a/src/types/tail.ts b/src/types/tail.ts index 1c00a34c..c9faf358 100644 --- a/src/types/tail.ts +++ b/src/types/tail.ts @@ -3,9 +3,10 @@ * @module tutils/types/Tail */ -import type IfArray from './if-array' -import type IfString from './if-string' -import type NumberString from './number-string' +import type EmptyArray from './empty-array' +import type EmptyString from './empty-string' +import type IfAny from './if-any' +import type Join from './join' /** * Returns the tail of `T`. @@ -16,20 +17,26 @@ import type NumberString from './number-string' * - all items in `T` after the first if `T` is an array * - all segments after the first delimiter in `T` if `T` is a string * - * @template T - Path to evaluate + * @todo examples + * + * @template T - Type to evaluate * @template D - String delimiter */ -type Tail = T extends string | readonly unknown[] - ? IfString< - T, - T extends `${NumberString}${D}${infer Last}` ? Last : T, - IfArray< - T, - unknown, - T extends [infer Elements, ...infer Last] ? Last : T, - T - > - > - : never +type Tail< + T extends string | readonly unknown[], + D extends string = EmptyString +> = IfAny< + T, + never, + T extends string + ? T extends Join<[string, D, infer R extends string], EmptyString> + ? R + : T + : T extends Readonly + ? T + : T extends readonly [T[0]?, ...infer R extends T[number][]] + ? R + : T +> export type { Tail as default } diff --git a/src/types/template-data.ts b/src/types/template-data.ts new file mode 100644 index 00000000..32592323 --- /dev/null +++ b/src/types/template-data.ts @@ -0,0 +1,19 @@ +/** + * @file Type Definitions - TemplateData + * @module tutils/types/TemplateData + */ + +import type NumberString from './number-string' +import type Primitive from './primitive' + +/** + * Object representing template data. + * + * @todo add more detail to documentation + * @todo examples + */ +type TemplateData = { + [K in NumberString]?: Exclude | TemplateData +} + +export type { TemplateData as default } diff --git a/src/types/trim-end.ts b/src/types/trim-end.ts index bca2fc34..209ee606 100644 --- a/src/types/trim-end.ts +++ b/src/types/trim-end.ts @@ -3,6 +3,7 @@ * @module tutils/types/TrimEnd */ +import type IfAny from './if-any' import type Whitespace from './whitespace' /** @@ -10,10 +11,26 @@ import type Whitespace from './whitespace' * * @see {@linkcode Whitespace} * + * @example + * type X = TrimEnd<'hello, world '> + * // 'hello, world' + * @example + * type X = TrimEnd<' hello'> + * // ' hello' + * @example + * type X = TrimEnd<'world'> + * // 'world' + * @example + * type X = TrimEnd + * // string + * @example + * type X = TrimEnd + * // never + * * @template T - String to trim */ type TrimEnd = T extends `${infer Rest}${Whitespace}` ? TrimEnd - : T + : IfAny export type { TrimEnd as default } diff --git a/src/types/trim-start.ts b/src/types/trim-start.ts index fb887e82..77ec5e76 100644 --- a/src/types/trim-start.ts +++ b/src/types/trim-start.ts @@ -3,6 +3,7 @@ * @module tutils/types/TrimStart */ +import type IfAny from './if-any' import type Whitespace from './whitespace' /** @@ -10,10 +11,26 @@ import type Whitespace from './whitespace' * * @see {@linkcode Whitespace} * + * @example + * type X = TrimStart<' hello, world'> + * // 'hello, world' + * @example + * type X = TrimStart<'hello '> + * // 'hello ' + * @example + * type X = TrimStart<'world'> + * // 'world' + * @example + * type X = TrimStart + * // string + * @example + * type X = TrimStart + * // never + * * @template T - String to trim */ type TrimStart = T extends `${Whitespace}${infer Rest}` ? TrimStart - : T + : IfAny export type { TrimStart as default } diff --git a/src/types/trim.ts b/src/types/trim.ts index e7adc586..c15e330e 100644 --- a/src/types/trim.ts +++ b/src/types/trim.ts @@ -3,15 +3,28 @@ * @module tutils/types/Trim */ -import type EnsureString from './ensure-string' +import type Stringify from './stringify' import type TrimEnd from './trim-end' import type TrimStart from './trim-start' /** * Removes leading and trailing whitespaces from string `T`. * + * @example + * type X = Trim<' hello, world '> + * // 'hello, world' + * @example + * type X = Trim<'hello'> + * // 'hello' + * @example + * type X = Trim + * // string + * @example + * type X = Trim + * // never + * * @template T - String to trim */ -type Trim = EnsureString>> +type Trim = Stringify>> export type { Trim as default } diff --git a/src/types/tuple-length.ts b/src/types/tuple-length.ts deleted file mode 100644 index 90f04215..00000000 --- a/src/types/tuple-length.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @file Type Definitions - TupleLength - * @module tutils/types/TupleLength - */ - -/** - * Returns the length of a [tuple][1]. - * - * [1]: https://www.codecademy.com/resources/docs/typescript/tuples - * - * @template T - Type to evaluate - */ -type TupleLength = NonNullable extends readonly unknown[] - ? NonNullable['length'] - : number - -export type { TupleLength as default } diff --git a/src/types/tuple-to-union.ts b/src/types/tuple-to-union.ts deleted file mode 100644 index 47dbad1b..00000000 --- a/src/types/tuple-to-union.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @file Type Definitions - TupleToUnion - * @module tutils/types/TupleToUnion - */ - -/** - * Converts a [tuple][1] to a [union][2]. - * - * [1]: https://www.codecademy.com/resources/docs/typescript/tuples - * [2]: https://www.typescriptlang.org/docs/handbook/unions-and-intersections#union-types - * - * @template T - Type to evaluate - */ -type TupleToUnion = T extends readonly unknown[] ? T[number] : never - -export type { TupleToUnion as default } diff --git a/src/types/union-to-intersection.ts b/src/types/union-to-intersection.ts index b8b70ca4..877f16cd 100644 --- a/src/types/union-to-intersection.ts +++ b/src/types/union-to-intersection.ts @@ -6,14 +6,33 @@ import type Fn from './fn' /** - * Converts union type `U` to an intersection type. + * Convert a union to an intersection using [distributive conditional types][1]. * - * Returns `U` if `U` is not a union type. + * [1]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types * - * @template U - Type to evaluate + * @see https://stackoverflow.com/a/50375286/2172153 + * @see https://www.typescriptlang.org/docs/handbook/unions-and-intersections + * + * @example + * type X = UnionToIntersection<{ x: 0 } | { y: 0 }> + * // { x: 0 } & { y: 0 } + * @example + * type X = UnionToIntersection<{ x: 0 }> + * // { x: 0 } + * @example + * type X = UnionToIntersection + * // any + * @example + * type X = UnionToIntersection + * // unknown + * @example + * type X = UnionToIntersection + * // unknown + * + * @template T - Type to evaluate */ -type UnionToIntersection = ( - U extends unknown ? Fn<[U], void> : never +type UnionToIntersection = ( + T extends unknown ? Fn<[T], void> : never ) extends Fn<[infer I], void> ? I : never diff --git a/src/types/union-to-tuple.ts b/src/types/union-to-tuple.ts index 39f7de10..e34be156 100644 --- a/src/types/union-to-tuple.ts +++ b/src/types/union-to-tuple.ts @@ -3,20 +3,37 @@ * @module tutils/types/UnionToTuple */ +import type EmptyArray from './empty-array' import type Fn from './fn' import type UnionToIntersection from './union-to-intersection' /** - * Converts `U` to a [tuple][1] type. + * Convert a union to a [tuple][1]. * * [1]: https://www.codecademy.com/resources/docs/typescript/tuples * - * @template U - Type to evaluate + * @example + * type X = UnionToTuple<0 | 1 | 2> + * // [0, 1, 2] + * @example + * type X = UnionToTuple + * // [number, string] + * @example + * type X = UnionToTuple + * // [any] + * @example + * type X = UnionToTuple + * // [] + * @example + * type X = UnionToTuple + * // [unknown] + * + * @template T - Type to evaluate */ -type UnionToTuple = UnionToIntersection< - U extends never ? never : Fn<[U], U> +type UnionToTuple = UnionToIntersection< + T extends never ? never : Fn<[T], T> > extends Fn<[never], infer W> - ? [...UnionToTuple>, W] - : [] + ? [...UnionToTuple>, W] + : EmptyArray export type { UnionToTuple as default } diff --git a/src/types/unwrap-numeric.ts b/src/types/unwrap-numeric.ts new file mode 100644 index 00000000..bc756223 --- /dev/null +++ b/src/types/unwrap-numeric.ts @@ -0,0 +1,54 @@ +/** + * @file Type Definitions - UnwrapNumeric + * @module tutils/types/UnwrapNumeric + */ + +import type IfAny from './if-any' +import type NegativeNumeric from './numeric-negative' + +/** + * Extracts a number from a numeric. + * + * A numeric is a string containing only numbers (not including the leading `-` + * if the numeric is negative). + * + * @example + * type X = UnwrapNumeric<'-3'> + * // -3 + * @example + * type X = UnwrapNumeric<'13'> + * // 13 + * @example + * type X = UnwrapNumeric<'-0'> + * // 0 + * @example + * type X = UnwrapNumeric + * // number + * @example + * type X = UnwrapNumeric + * // number + * @example + * type X = UnwrapNumeric + * // number + * @example + * type X = UnwrapNumeric + * // never + * @example + * type X = UnwrapNumeric + * // never + * + * @template T - Type to evaluate + */ +type UnwrapNumeric = IfAny< + T, + number, + T extends `-${infer N extends 0}` + ? N + : T extends `${infer N extends number}` + ? N + : T extends NegativeNumeric + ? number + : never +> + +export type { UnwrapNumeric as default } diff --git a/src/utils/__tests__/is-booleanish.spec.ts b/src/utils/__tests__/is-booleanish.spec.ts index 91f885e0..4a32b4a6 100644 --- a/src/utils/__tests__/is-booleanish.spec.ts +++ b/src/utils/__tests__/is-booleanish.spec.ts @@ -7,16 +7,18 @@ import testSubject from '../is-booleanish' describe('unit:utils/isBooleanish', () => { it('should return false if value is not booleanish', () => { - expect(testSubject(faker.number.int())).to.be.false + expect(testSubject(faker.number.int({ min: 3 }))).to.be.false }) it('should return true if value is booleanish', () => { // Arrange const cases: Parameters[] = [ - [false], - [true], + [0], + [1], ['false'], - ['true'] + ['true'], + [false], + [true] ] // Act + Expect diff --git a/src/utils/is-booleanish.ts b/src/utils/is-booleanish.ts index a577146d..76ddce4b 100644 --- a/src/utils/is-booleanish.ts +++ b/src/utils/is-booleanish.ts @@ -8,13 +8,13 @@ import includes from './includes' import isBoolean from './is-boolean' /** - * Checks if `value` is a boolean, `'false'`, or `'true'`. + * Checks if `value` is {@linkcode Booleanish}. * * @param {unknown} value - Value to check * @return {value is Booleanish} `true` if `value` is {@linkcode Booleanish} */ const isBooleanish = (value: unknown): value is Booleanish => { - return isBoolean(value) || includes(['false', 'true'], value) + return isBoolean(value) || includes(['false', 'true', 0, 1], value) } export default isBooleanish diff --git a/vitest.config.ts b/vitest.config.ts index 70166cb5..a42a81b3 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -57,11 +57,11 @@ const config: UserConfigExport = defineConfig((): UserConfig => { clean: true, cleanOnRerun: true, exclude: [ - '**/__mocks__/**', - '**/__tests__/**', + '**/__mocks__/', + '**/__tests__/', '**/index.ts', - 'src/interfaces/', - 'src/types/', + '**/interfaces/', + '**/types/', 'src/utils/*.options.ts' ], extension: ['.ts'], @@ -75,8 +75,11 @@ const config: UserConfigExport = defineConfig((): UserConfig => { environmentOptions: {}, globalSetup: [], globals: true, + hideSkippedTests: false, hookTimeout: 10 * 1000, - include: [`**/__tests__/*.spec${LINT_STAGED ? ',spec-d' : ''}.{ts,tsx}`], + include: [ + `**/__tests__/*.${LINT_STAGED ? '{spec,spec-d}' : 'spec'}.{ts,tsx}` + ], isolate: true, mockReset: true, outputFile: { json: './__tests__/report.json' }, diff --git a/yarn.lock b/yarn.lock index 8d2b0466..c7fab12a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,6 +34,13 @@ __metadata: languageName: node linkType: hard +"@andrewbranch/untar.js@npm:^1.0.0": + version: 1.0.2 + resolution: "@andrewbranch/untar.js@npm:1.0.2" + checksum: 138d4020846d83e841835a24f7ead6d97147d245988fbb1c730bea0e898d7075274966528f5b61403427e84680ce1d1cab880d357810e249ee81a260021c9fab + languageName: node + linkType: hard + "@ardatan/sync-fetch@larsgw/sync-fetch#head=worker_threads": version: 0.3.1 resolution: "@ardatan/sync-fetch@https://github.com/larsgw/sync-fetch.git#commit=3fb71aeed5ff6655421064c4e4b234f524896cd2" @@ -44,6 +51,35 @@ __metadata: languageName: node linkType: hard +"@arethetypeswrong/cli@npm:0.4.2": + version: 0.4.2 + resolution: "@arethetypeswrong/cli@npm:0.4.2" + dependencies: + "@arethetypeswrong/core": "npm:0.4.1" + chalk: "npm:^4.1.2" + cli-table3: "npm:^0.6.3" + commander: "npm:^10.0.1" + marked: "npm:^5.1.0" + marked-terminal: "npm:^5.2.0" + bin: + attw: dist/index.js + checksum: 3aa5c00cfb5de7206ed424da200f3d38b108fc8ebf779cf202a42b19309dd00add7ff0bd587ea5d0450bff5f0c490085989047d88041305b92f5a80e77b6494c + languageName: node + linkType: hard + +"@arethetypeswrong/core@npm:0.4.1": + version: 0.4.1 + resolution: "@arethetypeswrong/core@npm:0.4.1" + dependencies: + "@andrewbranch/untar.js": "npm:^1.0.0" + fetch-ponyfill: "npm:^7.1.0" + fflate: "npm:^0.7.4" + typescript: "npm:^5.1.3" + validate-npm-package-name: "npm:^5.0.0" + checksum: 24251ea2ea7ab9b369f2d9c913406b09f0d480ea9bb549f05f511e0cdcf6143bb1f95345f44558e0c5092fa2116139d8d86229f36a25bb02641aedc9cad783a6 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.18.6": version: 7.18.6 resolution: "@babel/code-frame@npm:7.18.6" @@ -198,12 +234,19 @@ __metadata: languageName: node linkType: hard -"@commitlint/cli@npm:17.6.3": - version: 17.6.3 - resolution: "@commitlint/cli@npm:17.6.3" +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: 5e08870799494f68e5b3b79e9a337bbf5fd7e634904fbbe642769921bf158fe458c41c888f88edf051b78c5325e3339970f00b24e31421c3480bb58f02687218 + languageName: node + linkType: hard + +"@commitlint/cli@npm:17.6.5": + version: 17.6.5 + resolution: "@commitlint/cli@npm:17.6.5" dependencies: "@commitlint/format": "npm:^17.4.4" - "@commitlint/lint": "npm:^17.6.3" + "@commitlint/lint": "npm:^17.6.5" "@commitlint/load": "npm:^17.5.0" "@commitlint/read": "npm:^17.5.1" "@commitlint/types": "npm:^17.4.4" @@ -214,7 +257,7 @@ __metadata: yargs: "npm:^17.0.0" bin: commitlint: cli.js - checksum: 118e13847416ff9ccced022a81d17019ac7d370cd572ecdbfd76e9d96664b7f898a48b179f48d0d8a2290c82fe583950fa7a4d93b2398e6d7b6fe37b647879a9 + checksum: 9bc387ae28dea3fef4b67beb4a2527e6aaa2cf1597f48e3f374e2841e78689f32fd9110a0e95d6b6cb2c6b514d0c9dab7cb0df9d24718480802db32ea3d7a100 languageName: node linkType: hard @@ -259,25 +302,25 @@ __metadata: languageName: node linkType: hard -"@commitlint/is-ignored@npm:^17.6.3": - version: 17.6.3 - resolution: "@commitlint/is-ignored@npm:17.6.3" +"@commitlint/is-ignored@npm:^17.6.6": + version: 17.6.6 + resolution: "@commitlint/is-ignored@npm:17.6.6" dependencies: "@commitlint/types": "npm:^17.4.4" - semver: "npm:7.5.0" - checksum: a7267396d64d10ded862145732a82b730906ea746da6ee22a355c0bd25e197ad15ce1d8ff065b4d5eda9d4e42a484c53b963963dffe74d807af4871f38193dc5 + semver: "npm:7.5.2" + checksum: 529f45bcb81a090c399e160db2739a87a6eafaea617a621a2bb3c5c6dd70f039dd8e99470838819ca76d6268294c3f6b8494e7a0a705507c7579601bc08bbeea languageName: node linkType: hard -"@commitlint/lint@npm:^17.6.3": - version: 17.6.3 - resolution: "@commitlint/lint@npm:17.6.3" +"@commitlint/lint@npm:^17.6.5": + version: 17.6.6 + resolution: "@commitlint/lint@npm:17.6.6" dependencies: - "@commitlint/is-ignored": "npm:^17.6.3" - "@commitlint/parse": "npm:^17.4.4" - "@commitlint/rules": "npm:^17.6.1" + "@commitlint/is-ignored": "npm:^17.6.6" + "@commitlint/parse": "npm:^17.6.5" + "@commitlint/rules": "npm:^17.6.5" "@commitlint/types": "npm:^17.4.4" - checksum: 6ba065cf07b925b9203bf2ca2ff5a96c38d485f5271044a3db73e737483c7f568996cfcaeaaf963cd86fb39c93d9e49f64223b71e48d623a633ec47a511e5647 + checksum: c5cf6a77884007f750374e2e13089b8fe5839b40c85490a5bd19d5a7a7a789c63437acdd40a4b8e9f626f37a1cccebd9b854077c24826793ca26fef66c36e18a languageName: node linkType: hard @@ -310,14 +353,14 @@ __metadata: languageName: node linkType: hard -"@commitlint/parse@npm:^17.4.4": - version: 17.4.4 - resolution: "@commitlint/parse@npm:17.4.4" +"@commitlint/parse@npm:^17.6.5": + version: 17.6.5 + resolution: "@commitlint/parse@npm:17.6.5" dependencies: "@commitlint/types": "npm:^17.4.4" conventional-changelog-angular: "npm:^5.0.11" conventional-commits-parser: "npm:^3.2.2" - checksum: 354301638fe5349074729a063ed20ef7e5ab610cc1ff44937daf850528e76af5161b367213b600bff4f16e26ffa0a05706cf3fb02f3055cb69e8780133773290 + checksum: 017eb4ddd1398d4e65d26c9164beb0c6db1d1ab70ca0b411eb01ab8073c7e5b0dc4bbc5fb467b04ae6054505e793f70c1e8660a3c56a4700f47b3a1def19c5a8 languageName: node linkType: hard @@ -348,16 +391,16 @@ __metadata: languageName: node linkType: hard -"@commitlint/rules@npm:^17.6.1": - version: 17.6.1 - resolution: "@commitlint/rules@npm:17.6.1" +"@commitlint/rules@npm:^17.6.5": + version: 17.6.5 + resolution: "@commitlint/rules@npm:17.6.5" dependencies: "@commitlint/ensure": "npm:^17.4.4" "@commitlint/message": "npm:^17.4.2" "@commitlint/to-lines": "npm:^17.4.0" "@commitlint/types": "npm:^17.4.4" execa: "npm:^5.0.0" - checksum: 7e7c552fa925d39144ec48c4cd5f32580d23cb3bbac92d0776d5096a48bb26c3a956e99f1088b7d026a61e61c76cbb5fb3ce97d6367bad501401f716c0fd24ed + checksum: 29ee0da954157ef7a93ca117f06f202ac6e46d89fbc7bf6303342a66cbcd4c25d7a85368e967fd5677b04432edfe2e262fb3967e99aeda8ab8dbbc316e62eca6 languageName: node linkType: hard @@ -826,6 +869,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/android-arm64@npm:0.18.6" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm@npm:0.17.19" @@ -833,6 +883,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/android-arm@npm:0.18.6" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-x64@npm:0.17.19" @@ -840,6 +897,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/android-x64@npm:0.18.6" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-arm64@npm:0.17.19" @@ -847,6 +911,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/darwin-arm64@npm:0.18.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-x64@npm:0.17.19" @@ -854,6 +925,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/darwin-x64@npm:0.18.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-arm64@npm:0.17.19" @@ -861,6 +939,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/freebsd-arm64@npm:0.18.6" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-x64@npm:0.17.19" @@ -868,6 +953,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/freebsd-x64@npm:0.18.6" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm64@npm:0.17.19" @@ -875,6 +967,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-arm64@npm:0.18.6" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm@npm:0.17.19" @@ -882,6 +981,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-arm@npm:0.18.6" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ia32@npm:0.17.19" @@ -889,6 +995,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-ia32@npm:0.18.6" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-loong64@npm:0.17.19" @@ -896,6 +1009,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-loong64@npm:0.18.6" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-mips64el@npm:0.17.19" @@ -903,6 +1023,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-mips64el@npm:0.18.6" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ppc64@npm:0.17.19" @@ -910,6 +1037,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-ppc64@npm:0.18.6" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-riscv64@npm:0.17.19" @@ -917,6 +1051,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-riscv64@npm:0.18.6" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-s390x@npm:0.17.19" @@ -924,6 +1065,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-s390x@npm:0.18.6" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-x64@npm:0.17.19" @@ -931,6 +1079,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/linux-x64@npm:0.18.6" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/netbsd-x64@npm:0.17.19" @@ -938,6 +1093,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/netbsd-x64@npm:0.18.6" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/openbsd-x64@npm:0.17.19" @@ -945,6 +1107,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/openbsd-x64@npm:0.18.6" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/sunos-x64@npm:0.17.19" @@ -952,6 +1121,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/sunos-x64@npm:0.18.6" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-arm64@npm:0.17.19" @@ -959,6 +1135,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/win32-arm64@npm:0.18.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-ia32@npm:0.17.19" @@ -966,6 +1149,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/win32-ia32@npm:0.18.6" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-x64@npm:0.17.19" @@ -973,6 +1163,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.18.6": + version: 0.18.6 + resolution: "@esbuild/win32-x64@npm:0.18.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -1008,17 +1205,17 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.41.0": - version: 8.41.0 - resolution: "@eslint/js@npm:8.41.0" - checksum: 7f4b1d920b5216c2c6a14574e5fc970c7858784c007358e5271c17bd8a8c51fcd5972ac4904c082722bb0fdf404bfe67f7d518c4b1e90ec77a82c92b3ef1e484 +"@eslint/js@npm:8.43.0": + version: 8.43.0 + resolution: "@eslint/js@npm:8.43.0" + checksum: 3231c88eee1cb8c8b77771eed89d99a26e8d813bc0ada47fbdadaff1a16cbd74000633f4411a74c3d7cc71629f78da177060b369ce6aac461568712175c2ce8c languageName: node linkType: hard -"@faker-js/faker@npm:8.0.1": - version: 8.0.1 - resolution: "@faker-js/faker@npm:8.0.1" - checksum: ec118ba2bb0e0ee4e1bd4c6a06001ad0e7737814b768c44c4024d4ff7b9b57449be77a14becc7462ea2e63d38f6f4fb287cef343a101dbd8f98e6ee0609f5ba0 +"@faker-js/faker@npm:8.0.2": + version: 8.0.2 + resolution: "@faker-js/faker@npm:8.0.2" + checksum: 9114ce0ad7fa08935874142ca8121fcaba606743bb39446c7e00638c185a5171d4b1de81b90530cc8bb7faf233f935d50a1dfe7b547982e060ee9404b4b7baa8 languageName: node linkType: hard @@ -1285,8 +1482,9 @@ __metadata: version: 0.0.0-use.local resolution: "@flex-development/tutils@workspace:." dependencies: - "@commitlint/cli": "npm:17.6.3" - "@faker-js/faker": "npm:8.0.1" + "@arethetypeswrong/cli": "npm:0.4.2" + "@commitlint/cli": "npm:17.6.5" + "@faker-js/faker": "npm:8.0.2" "@flex-development/commitlint-config": "npm:1.0.1" "@flex-development/decorator-regex": "npm:1.0.0" "@flex-development/esm-types": "npm:1.0.0" @@ -1300,22 +1498,22 @@ __metadata: "@types/chai-quantifiers": "npm:1.0.1" "@types/chai-string": "npm:1.4.2" "@types/conventional-changelog": "npm:3.1.1" - "@types/conventional-changelog-core": "npm:4.2.1" - "@types/conventional-changelog-writer": "npm:4.0.2" + "@types/conventional-changelog-core": "npm:4.2.2" + "@types/conventional-changelog-writer": "npm:4.0.3" "@types/conventional-recommended-bump": "npm:6.1.0" "@types/dateformat": "npm:5.0.0" - "@types/eslint": "npm:8.37.0" + "@types/eslint": "npm:8.40.2" "@types/fs-extra": "npm:11.0.1" "@types/git-raw-commits": "npm:2.0.1" "@types/is-ci": "npm:3.0.0" "@types/node-notifier": "npm:8.0.2" - "@types/prettier": "npm:2.7.2" + "@types/prettier": "npm:2.7.3" "@types/semver": "npm:7.5.0" - "@typescript-eslint/eslint-plugin": "npm:5.59.7" - "@typescript-eslint/parser": "npm:5.59.7" + "@typescript-eslint/eslint-plugin": "npm:5.60.0" + "@typescript-eslint/parser": "npm:5.60.0" "@vates/toggle-scripts": "npm:1.0.0" - "@vitest/coverage-c8": "npm:0.31.1" - "@vitest/ui": "npm:0.31.1" + "@vitest/coverage-c8": "npm:0.32.2" + "@vitest/ui": "npm:0.32.2" add-stream: "npm:1.0.0" chai: "npm:5.0.0-alpha.0" chai-each: "npm:0.0.1" @@ -1329,22 +1527,22 @@ __metadata: cross-env: "npm:7.0.3" cspell: "npm:7.0.0-alpha.2" dequal: "npm:2.0.3" - esbuild: "npm:0.17.19" - eslint: "npm:8.41.0" + esbuild: "npm:0.18.6" + eslint: "npm:8.43.0" eslint-config-prettier: "npm:8.8.0" eslint-plugin-chai-expect: "npm:3.0.0" eslint-plugin-jest-formatting: "npm:3.1.0" eslint-plugin-jsdoc: "npm:44.1.0" - eslint-plugin-jsonc: "npm:2.8.0" + eslint-plugin-jsonc: "npm:2.9.0" eslint-plugin-markdown: "npm:3.0.0" eslint-plugin-markdownlint: "npm:0.4.1" eslint-plugin-node: "npm:11.1.0" eslint-plugin-prettier: "npm:4.2.1" eslint-plugin-promise: "npm:6.1.1" eslint-plugin-unicorn: "npm:47.0.0" - eslint-plugin-yml: "npm:1.7.0" - graphql: "npm:16.6.0" - graphql-config: "npm:4.5.0" + eslint-plugin-yml: "npm:1.8.0" + graphql: "npm:16.7.0" + graphql-config: "npm:5.0.2" growl: "npm:1.10.5" husky: "npm:8.0.3" is-ci: "npm:3.0.1" @@ -1358,16 +1556,16 @@ __metadata: pretty-format: "npm:29.5.0" pupa: "npm:3.1.0" sade: "npm:1.8.1" - semver: "npm:7.5.1" + semver: "npm:7.5.2" serve: "npm:14.2.0" tempfile: "npm:5.0.0" trash-cli: "npm:5.0.0" ts-dedent: "npm:2.2.0" - typescript: "npm:5.1.0-beta" + typescript: "npm:5.1.6" version-bump-prompt: "npm:6.1.0" - vite: "npm:4.3.8" + vite: "npm:4.3.9" vite-tsconfig-paths: "npm:4.2.0" - vitest: "npm:0.31.1" + vitest: "npm:0.32.2" vitest-github-actions-reporter: "npm:0.10.0" yaml-eslint-parser: "npm:1.2.2" peerDependencies: @@ -1420,6 +1618,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/batch-execute@npm:^9.0.0": + version: 9.0.0 + resolution: "@graphql-tools/batch-execute@npm:9.0.0" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + dataloader: "npm:^2.2.2" + tslib: "npm:^2.4.0" + value-or-promise: "npm:^1.0.12" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: a55c4d827d6ddebeac66ff4998c98590104c6def573b9ebc5375416802c34a0a5f7a392fbbf97052d84e09990bd7e1c0048653ee6d0351b681d10b39c07ce212 + languageName: node + linkType: hard + "@graphql-tools/code-file-loader@npm:^7.3.6": version: 7.3.16 resolution: "@graphql-tools/code-file-loader@npm:7.3.16" @@ -1452,6 +1664,23 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/delegate@npm:^10.0.0": + version: 10.0.0 + resolution: "@graphql-tools/delegate@npm:10.0.0" + dependencies: + "@graphql-tools/batch-execute": "npm:^9.0.0" + "@graphql-tools/executor": "npm:^1.0.0" + "@graphql-tools/schema": "npm:^10.0.0" + "@graphql-tools/utils": "npm:^10.0.0" + dataloader: "npm:^2.2.2" + tslib: "npm:^2.5.0" + value-or-promise: "npm:^1.0.12" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 3f6b605a47d6b1f4b82c288f7a36eb8c6269bfadbaf6b373afb14978d6828e1a04e470e160d5a532271ef6f05c0eb2ba785e515454545b1304a70342a1f33931 + languageName: node + linkType: hard + "@graphql-tools/executor-graphql-ws@npm:0.0.7": version: 0.0.7 resolution: "@graphql-tools/executor-graphql-ws@npm:0.0.7" @@ -1469,6 +1698,23 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/executor-graphql-ws@npm:^1.0.0": + version: 1.0.1 + resolution: "@graphql-tools/executor-graphql-ws@npm:1.0.1" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + "@repeaterjs/repeater": "npm:3.0.4" + "@types/ws": "npm:^8.0.0" + graphql-ws: "npm:5.14.0" + isomorphic-ws: "npm:5.0.0" + tslib: "npm:^2.4.0" + ws: "npm:8.13.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: baa7485330624c1d00c634d3f2e3f4cb84952c72301b2489cc59b661ccab2ab80d1d6ef91e531d9b8ef182018272692f340bb000c9e542b432297d0866f24d9d + languageName: node + linkType: hard + "@graphql-tools/executor-http@npm:0.1.1": version: 0.1.1 resolution: "@graphql-tools/executor-http@npm:0.1.1" @@ -1487,6 +1733,24 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/executor-http@npm:^1.0.0": + version: 1.0.0 + resolution: "@graphql-tools/executor-http@npm:1.0.0" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + "@repeaterjs/repeater": "npm:^3.0.4" + "@whatwg-node/fetch": "npm:^0.9.0" + dset: "npm:^3.1.2" + extract-files: "npm:^11.0.0" + meros: "npm:^1.2.1" + tslib: "npm:^2.4.0" + value-or-promise: "npm:^1.0.12" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 2fd8ae560d19b30afb3e135e0172ec8b769df3b9e1cf7ccac359c0764567309049279308a9b1e3a0006f6626927b715056686f2f95fb82a3a79eabf620032caf + languageName: node + linkType: hard + "@graphql-tools/executor-legacy-ws@npm:0.0.6": version: 0.0.6 resolution: "@graphql-tools/executor-legacy-ws@npm:0.0.6" @@ -1502,6 +1766,21 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/executor-legacy-ws@npm:^1.0.0": + version: 1.0.1 + resolution: "@graphql-tools/executor-legacy-ws@npm:1.0.1" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + "@types/ws": "npm:^8.0.0" + isomorphic-ws: "npm:5.0.0" + tslib: "npm:^2.4.0" + ws: "npm:8.13.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 82051e6a315b8337b9b9e33d75e1bfd994b1cc2262f98081f88bc06df33832c82da4c2690a4a3ae56321bd65bb4580d8042a98e1162a004dfb2235fdff8c0fb0 + languageName: node + linkType: hard + "@graphql-tools/executor@npm:0.0.12": version: 0.0.12 resolution: "@graphql-tools/executor@npm:0.0.12" @@ -1517,6 +1796,21 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/executor@npm:^1.0.0": + version: 1.1.0 + resolution: "@graphql-tools/executor@npm:1.1.0" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + "@graphql-typed-document-node/core": "npm:3.2.0" + "@repeaterjs/repeater": "npm:^3.0.4" + tslib: "npm:^2.4.0" + value-or-promise: "npm:^1.0.12" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 543f5d7bea1da6b59444e91ac32a1e486ad06802b14a27baf455dd023d3021ab00df7aec468977a9a9b550f68d575376cecf79069b7efde0109dee85bfa01119 + languageName: node + linkType: hard + "@graphql-tools/graphql-file-loader@npm:^7.3.7": version: 7.5.14 resolution: "@graphql-tools/graphql-file-loader@npm:7.5.14" @@ -1532,6 +1826,21 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/graphql-file-loader@npm:^8.0.0": + version: 8.0.0 + resolution: "@graphql-tools/graphql-file-loader@npm:8.0.0" + dependencies: + "@graphql-tools/import": "npm:7.0.0" + "@graphql-tools/utils": "npm:^10.0.0" + globby: "npm:^11.0.3" + tslib: "npm:^2.4.0" + unixify: "npm:^1.0.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 025c9aad22e2377dda695906a6572b7427d502ff55e6a76a68e11521f346df42e02152844deb271e876efee8bb0aff0a7a987aa715b5f5bf750dd4a17b5d0230 + languageName: node + linkType: hard + "@graphql-tools/graphql-tag-pluck@npm:7.4.3, @graphql-tools/graphql-tag-pluck@npm:^7.3.6": version: 7.4.3 resolution: "@graphql-tools/graphql-tag-pluck@npm:7.4.3" @@ -1561,6 +1870,19 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/import@npm:7.0.0": + version: 7.0.0 + resolution: "@graphql-tools/import@npm:7.0.0" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + resolve-from: "npm:5.0.0" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 218a4df21eb4ff85c224e3b3734c53b4877b2aed424adf863ebd0caa8df04b4373ccbc111d03f3ce2d7eca29835c08f3bd802d0b96dd6b335a8348628940effe + languageName: node + linkType: hard + "@graphql-tools/json-file-loader@npm:^7.3.7": version: 7.4.15 resolution: "@graphql-tools/json-file-loader@npm:7.4.15" @@ -1575,6 +1897,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/json-file-loader@npm:^8.0.0": + version: 8.0.0 + resolution: "@graphql-tools/json-file-loader@npm:8.0.0" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + globby: "npm:^11.0.3" + tslib: "npm:^2.4.0" + unixify: "npm:^1.0.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: fbf65dc8878fc8196a78eab1181122592d0cf1f5dc1b94e69d219f9430f35f228a854d15814ee883e5e9e6f147bfa3937ae87804fb15e61345acd176916f4054 + languageName: node + linkType: hard + "@graphql-tools/load@npm:^7.5.5": version: 7.8.10 resolution: "@graphql-tools/load@npm:7.8.10" @@ -1589,6 +1925,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/load@npm:^8.0.0": + version: 8.0.0 + resolution: "@graphql-tools/load@npm:8.0.0" + dependencies: + "@graphql-tools/schema": "npm:^10.0.0" + "@graphql-tools/utils": "npm:^10.0.0" + p-limit: "npm:3.1.0" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 57ea98a05fd1790788164d17965bd4adcdba6a5b969fdc67692dbdfa17fe65210f5f8580772b8eaf0d06daa5aaa3626ec296fd7e14e6e53626858e69930e8ba9 + languageName: node + linkType: hard + "@graphql-tools/merge@npm:8.3.16, @graphql-tools/merge@npm:^8.2.6": version: 8.3.16 resolution: "@graphql-tools/merge@npm:8.3.16" @@ -1601,6 +1951,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/merge@npm:^9.0.0": + version: 9.0.0 + resolution: "@graphql-tools/merge@npm:9.0.0" + dependencies: + "@graphql-tools/utils": "npm:^10.0.0" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 45c92817d0256c92f0b2354eb848b0cf1c2ace74200f9a6a0f8f048fd3b53ed28be369bd7aa6bf40fa0d89df2e9a21d707208a23370bc684018d583bcc864a7f + languageName: node + linkType: hard + "@graphql-tools/schema@npm:9.0.14": version: 9.0.14 resolution: "@graphql-tools/schema@npm:9.0.14" @@ -1615,6 +1977,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/schema@npm:^10.0.0": + version: 10.0.0 + resolution: "@graphql-tools/schema@npm:10.0.0" + dependencies: + "@graphql-tools/merge": "npm:^9.0.0" + "@graphql-tools/utils": "npm:^10.0.0" + tslib: "npm:^2.4.0" + value-or-promise: "npm:^1.0.12" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 1b8ed9cc20205be9a56593e96082ab65e6ad8b3e414be7a7d6a3ec8f226ea7188350bdc3922b85e46cd69393a0f274988fcfad259f0dd87dd1b5df5ae44d00b1 + languageName: node + linkType: hard + "@graphql-tools/url-loader@npm:^7.9.7": version: 7.17.5 resolution: "@graphql-tools/url-loader@npm:7.17.5" @@ -1638,6 +2014,29 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/url-loader@npm:^8.0.0": + version: 8.0.0 + resolution: "@graphql-tools/url-loader@npm:8.0.0" + dependencies: + "@ardatan/sync-fetch": "npm:^0.0.1" + "@graphql-tools/delegate": "npm:^10.0.0" + "@graphql-tools/executor-graphql-ws": "npm:^1.0.0" + "@graphql-tools/executor-http": "npm:^1.0.0" + "@graphql-tools/executor-legacy-ws": "npm:^1.0.0" + "@graphql-tools/utils": "npm:^10.0.0" + "@graphql-tools/wrap": "npm:^10.0.0" + "@types/ws": "npm:^8.0.0" + "@whatwg-node/fetch": "npm:^0.9.0" + isomorphic-ws: "npm:^5.0.0" + tslib: "npm:^2.4.0" + value-or-promise: "npm:^1.0.11" + ws: "npm:^8.12.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 1b3bc4f6121b19046664f3ec4e89cddfcfdad1233160d2da2c7d0b3770a48dab95870f872cf1958bd44cd3adb65c1df032bdab4aa202af1d2f8f9ec2494e62c7 + languageName: node + linkType: hard + "@graphql-tools/utils@npm:9.1.4, @graphql-tools/utils@npm:^9.0.0": version: 9.1.4 resolution: "@graphql-tools/utils@npm:9.1.4" @@ -1649,6 +2048,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/utils@npm:^10.0.0": + version: 10.0.1 + resolution: "@graphql-tools/utils@npm:10.0.1" + dependencies: + "@graphql-typed-document-node/core": "npm:^3.1.1" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 005894764cb0dfff20e0600c0e6f213de4b1f0a9915ad3b10d4719e11399340c01c22cb9f870740aec935047e2e79e09fb1cb371b14064288844219096bd0893 + languageName: node + linkType: hard + "@graphql-tools/wrap@npm:9.3.3": version: 9.3.3 resolution: "@graphql-tools/wrap@npm:9.3.3" @@ -1664,6 +2075,21 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/wrap@npm:^10.0.0": + version: 10.0.0 + resolution: "@graphql-tools/wrap@npm:10.0.0" + dependencies: + "@graphql-tools/delegate": "npm:^10.0.0" + "@graphql-tools/schema": "npm:^10.0.0" + "@graphql-tools/utils": "npm:^10.0.0" + tslib: "npm:^2.4.0" + value-or-promise: "npm:^1.0.12" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 40b80f7f0d12f121524b99597a746ad48216596f5ef9f390f8acccd2e9f3b04fd56a83fae1973f338fb2b549551af63d120ceebafb29e630cd693debaa314bfe + languageName: node + linkType: hard + "@graphql-typed-document-node/core@npm:3.1.1": version: 3.1.1 resolution: "@graphql-typed-document-node/core@npm:3.1.1" @@ -1673,14 +2099,23 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.8": - version: 0.11.8 - resolution: "@humanwhocodes/config-array@npm:0.11.8" +"@graphql-typed-document-node/core@npm:3.2.0, @graphql-typed-document-node/core@npm:^3.1.1": + version: 3.2.0 + resolution: "@graphql-typed-document-node/core@npm:3.2.0" + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 9775c4e54324d6ce6ad505940a0c351ee42899088f436702476b3b93eaa60f7f646c459657d08a9e8793a91067cd55c18e1091bf2045b346f2e55fb37a1e8baa + languageName: node + linkType: hard + +"@humanwhocodes/config-array@npm:^0.11.10": + version: 0.11.10 + resolution: "@humanwhocodes/config-array@npm:0.11.10" dependencies: "@humanwhocodes/object-schema": "npm:^1.2.1" debug: "npm:^4.1.1" minimatch: "npm:^3.0.5" - checksum: 010892ba3c237e96562df1f21a7e04b611274f2c91b4df6c8263eb7d2ffcec3a5bfcab67b13d9c4acc8a2e3f94cb61d7ced772ecd445b226fb41b88c93e9194c + checksum: d8b3afa90dc46f4cbad48dd54e9bb39918a01fbfb16cc931fb55db8c967c1575fc21d4a5f8055f4728697246eafafc95e23c97370ff30ada621f4b581bed3f9d languageName: node linkType: hard @@ -1911,7 +2346,7 @@ __metadata: languageName: node linkType: hard -"@repeaterjs/repeater@npm:3.0.4": +"@repeaterjs/repeater@npm:3.0.4, @repeaterjs/repeater@npm:^3.0.4": version: 3.0.4 resolution: "@repeaterjs/repeater@npm:3.0.4" checksum: 86a89cbd91055e912e80334ded2ae5c44408747483d5ef237b5f72640e4905c6ec34d969ef7423a020fa49a28a9e28528dfdefabe4bf76ec84c1afaedfc777d2 @@ -2024,9 +2459,9 @@ __metadata: languageName: node linkType: hard -"@types/conventional-changelog-core@npm:*, @types/conventional-changelog-core@npm:4.2.1": - version: 4.2.1 - resolution: "@types/conventional-changelog-core@npm:4.2.1" +"@types/conventional-changelog-core@npm:*, @types/conventional-changelog-core@npm:4.2.2": + version: 4.2.2 + resolution: "@types/conventional-changelog-core@npm:4.2.2" dependencies: "@types/conventional-changelog-writer": "npm:*" "@types/conventional-commits-parser": "npm:*" @@ -2034,17 +2469,17 @@ __metadata: "@types/git-raw-commits": "npm:*" "@types/node": "npm:*" "@types/normalize-package-data": "npm:*" - checksum: 8c51268a157b84a9fd59beda3a9053e961e51d59fcb9d43ef1722611178a57b33b0d41f2cf2b4807c8dd17ba6fa4ebe27392916f1adc10c4740a4df08851305a + checksum: 5258078084b88b9051996db641f6acfacf7ef25ce01553a262c0b6315a0742fd0d33afa89535c290f9db161c1d064d5b606c79efe5c0def34d273bd12537b874 languageName: node linkType: hard -"@types/conventional-changelog-writer@npm:*, @types/conventional-changelog-writer@npm:4.0.2": - version: 4.0.2 - resolution: "@types/conventional-changelog-writer@npm:4.0.2" +"@types/conventional-changelog-writer@npm:*, @types/conventional-changelog-writer@npm:4.0.3": + version: 4.0.3 + resolution: "@types/conventional-changelog-writer@npm:4.0.3" dependencies: "@types/conventional-commits-parser": "npm:*" "@types/node": "npm:*" - checksum: 84b59aebd7b0a51c898d5a28ff32c9772124aeea693d13e6bf6ca6b51ee9ecb02e97fba9342ad0bad2e35cb3e53b984fd1d4555477bdd80c96a9b1459459507f + checksum: 77bd0573947262ddd5dfcad271655f01a1696e9716028af408f8798d395cef1ce78cb79f5e732a9078617e39e65b9f5181e28f216753452d206003c571d32976 languageName: node linkType: hard @@ -2087,13 +2522,13 @@ __metadata: languageName: node linkType: hard -"@types/eslint@npm:8.37.0": - version: 8.37.0 - resolution: "@types/eslint@npm:8.37.0" +"@types/eslint@npm:8.40.2": + version: 8.40.2 + resolution: "@types/eslint@npm:8.40.2" dependencies: "@types/estree": "npm:*" "@types/json-schema": "npm:*" - checksum: 5a1b547b4fbebeb173fd3e4a717de868021a14322cb7bd1bc2b432f3ac3cfec2e268623d24543a4ca7787fe64c59dd310a6ee329199e9f16b0d168d26a2da026 + checksum: 84f4da669a839830e3696494ec7ba659f7fa064eb87e3f1d4b60204b4db9c9093bbbd9f59b7bf46370bdba97cae0b959d4b2391c743d69297a8df5d100d35191 languageName: node linkType: hard @@ -2194,10 +2629,10 @@ __metadata: languageName: node linkType: hard -"@types/prettier@npm:2.7.2": - version: 2.7.2 - resolution: "@types/prettier@npm:2.7.2" - checksum: d4d09d291ec7017ed30cc2bac5a51dbd5de02e2d75389a4c724ac6c3d7bb99da3173f57247d832b8f83c154dc8006cbdc35e565c1f1bf6869718d25857e430db +"@types/prettier@npm:2.7.3": + version: 2.7.3 + resolution: "@types/prettier@npm:2.7.3" + checksum: 940c06437f23a12b88ae7d36c40a35dd3ee8cc22f8bc1cba972bcc69904162331ef2fdfafc9cc46762475047194321c0c40ee9285e779353116018c1eeae2b4c languageName: node linkType: hard @@ -2224,14 +2659,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/eslint-plugin@npm:5.59.7" +"@typescript-eslint/eslint-plugin@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.60.0" dependencies: "@eslint-community/regexpp": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:5.59.7" - "@typescript-eslint/type-utils": "npm:5.59.7" - "@typescript-eslint/utils": "npm:5.59.7" + "@typescript-eslint/scope-manager": "npm:5.60.0" + "@typescript-eslint/type-utils": "npm:5.60.0" + "@typescript-eslint/utils": "npm:5.60.0" debug: "npm:^4.3.4" grapheme-splitter: "npm:^1.0.4" ignore: "npm:^5.2.0" @@ -2244,43 +2679,43 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 9dedccbc485fcee43913fb0a31f8f35ff7b8e081aac929156c4c1a94327fd1015bdc8c891bc2b2765adb92ee466e144f5b40ede5128d98d784c39bea34c796b4 + checksum: 4bdd4067a226293ed2ed61226ac481a3256314aa6218b2535f8e07e019439bc6198a9b1d8aa5d762deccd268ec9cc378529bf4486ed07a67cc267c36dd89dfd7 languageName: node linkType: hard -"@typescript-eslint/parser@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/parser@npm:5.59.7" +"@typescript-eslint/parser@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/parser@npm:5.60.0" dependencies: - "@typescript-eslint/scope-manager": "npm:5.59.7" - "@typescript-eslint/types": "npm:5.59.7" - "@typescript-eslint/typescript-estree": "npm:5.59.7" + "@typescript-eslint/scope-manager": "npm:5.60.0" + "@typescript-eslint/types": "npm:5.60.0" + "@typescript-eslint/typescript-estree": "npm:5.60.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 88273a769c98ce30bf5ca9c28702a557e39fbed7ae8fefa170fb8541faa6b22395286d0be28d928de749a805e3309a7b06366e18f80e2137fee7a438fa5a1b85 + checksum: 71f421ad9d61d5520d6c0411164741535f4a84a0e1e04b0e4ca19e30d3cec42eef955cf4b2afbac19ae1b33be31ebbc1f024c1bcfddc71c5f83fd923a1683d58 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/scope-manager@npm:5.59.7" +"@typescript-eslint/scope-manager@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/scope-manager@npm:5.60.0" dependencies: - "@typescript-eslint/types": "npm:5.59.7" - "@typescript-eslint/visitor-keys": "npm:5.59.7" - checksum: 995c90b6c6fb714b89b0e925ecffde2e93a6fec08e7ead8995578000306e2c881f1ac57909cfa5c03e18bc558fb8ece91585eb8b4d88a2aefcc8872fd1ceb6b7 + "@typescript-eslint/types": "npm:5.60.0" + "@typescript-eslint/visitor-keys": "npm:5.60.0" + checksum: 42aa83bf008148d69f99419a8b48094863ad1d1655663c7cc8c213902f008e99bae9d9a8f2ed38a777984e2ea1ae96970ced382e0ccf16a480f12748062ae208 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/type-utils@npm:5.59.7" +"@typescript-eslint/type-utils@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/type-utils@npm:5.60.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:5.59.7" - "@typescript-eslint/utils": "npm:5.59.7" + "@typescript-eslint/typescript-estree": "npm:5.60.0" + "@typescript-eslint/utils": "npm:5.60.0" debug: "npm:^4.3.4" tsutils: "npm:^3.21.0" peerDependencies: @@ -2288,23 +2723,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 02c4ac018ab6cf2cd3873f13e42f664bb32cd18cb2392827a3ceac989c0acd5ec4cc4f2a6f493429297ba45db9809bfbc538528f0101c62463f3fdb0c81856aa + checksum: ab4cc164abd82ab137fbd53d6e347c723e18c0334c69f8ddc334a69cafb65394963e5b4dfc2388138745d805f2f0fe5ab4eeb7451850828b93a1cb22c998d0db languageName: node linkType: hard -"@typescript-eslint/types@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/types@npm:5.59.7" - checksum: c1ef7c479ff95c23b51b5313538699adefcf0fd10c636fdfe9eb7c0ecbfc38e3393c7b6a55f239786eff479a1045ccaf3543314057df390d95a66c89362476ed +"@typescript-eslint/types@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/types@npm:5.60.0" + checksum: b106fc5aedce1bf67926edf631bb3324d3b769c9ac62bd5cba6812edaf44b0366a39832e584edbe6114f4c36346e3212a35ec21023c52f9c53343468abbaa1d0 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/typescript-estree@npm:5.59.7" +"@typescript-eslint/typescript-estree@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.60.0" dependencies: - "@typescript-eslint/types": "npm:5.59.7" - "@typescript-eslint/visitor-keys": "npm:5.59.7" + "@typescript-eslint/types": "npm:5.60.0" + "@typescript-eslint/visitor-keys": "npm:5.60.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -2313,35 +2748,35 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: cb28381554a6aeb7faaec9ac67587722060fd79562ef3f66245253d188431e9fe998e7234b9bb711c31f96c214895a331740582081ea1eda5891afc277701022 + checksum: d24ef38fefdbd25a1b88073e82d4fa19fd877a2bf5f7a25c500809482bfdee2e4159f9945332632539ffd9939880a2f1ecc15d3f84dc09d105a9ab8c51e307c6 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/utils@npm:5.59.7" +"@typescript-eslint/utils@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/utils@npm:5.60.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@types/json-schema": "npm:^7.0.9" "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.59.7" - "@typescript-eslint/types": "npm:5.59.7" - "@typescript-eslint/typescript-estree": "npm:5.59.7" + "@typescript-eslint/scope-manager": "npm:5.60.0" + "@typescript-eslint/types": "npm:5.60.0" + "@typescript-eslint/typescript-estree": "npm:5.60.0" eslint-scope: "npm:^5.1.1" semver: "npm:^7.3.7" peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 3459bfdaf44ae8d4f0bc7325d3bc3f14538154793140b821ac076165738bb5ee6ced95cbfbdc67fea7061476ad5ed8c59c1c601f274986cc7fb3d578cc038b98 + checksum: 073ebe6826b07c0e3a5703c26b6513d33dd37247eb4e4ee9760d5c895dcba804504a6dc33df7a195e057878bf71c2092ce99e7731d8579b56d02ccba3c44a01c languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.59.7": - version: 5.59.7 - resolution: "@typescript-eslint/visitor-keys@npm:5.59.7" +"@typescript-eslint/visitor-keys@npm:5.60.0": + version: 5.60.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.60.0" dependencies: - "@typescript-eslint/types": "npm:5.59.7" + "@typescript-eslint/types": "npm:5.60.0" eslint-visitor-keys: "npm:^3.3.0" - checksum: 7123c7f3eb008a70c7ea56ef74d65876071234bef091a303b48141b38bdd557a40dbc87b6c22ff26d903e5883bd1e9a3bc2f9fa16ddbdccf84c75a59c22514fe + checksum: da3cbf47b0d69c00cf35ff575bdd9166ec08d8f0d5629cb487bd3186974c81afb4532d240736c04ff6b27df8308fdee955afa2c898e659c731f3294f674b2f62 languageName: node linkType: hard @@ -2354,9 +2789,9 @@ __metadata: languageName: node linkType: hard -"@vitest/coverage-c8@npm:0.31.1": - version: 0.31.1 - resolution: "@vitest/coverage-c8@npm:0.31.1" +"@vitest/coverage-c8@npm:0.32.2": + version: 0.32.2 + resolution: "@vitest/coverage-c8@npm:0.32.2" dependencies: "@ampproject/remapping": "npm:^2.2.1" c8: "npm:^7.13.0" @@ -2365,58 +2800,58 @@ __metadata: std-env: "npm:^3.3.2" peerDependencies: vitest: ">=0.30.0 <1" - checksum: 5b294b64abedd21a8b8cb549d3932b028b3484cbaf49b57170559b0a2da7a1342cfc90cb7cf8fbc31ccca435a47647c64cad9d988bbdb5e0b8c41f19061466a7 + checksum: 195e7372a93040803b3c40585003764eb9d5ec82522fae07432e30738899e5c87858c974d96e157b3ce17933fd47f93224ae30ab5db432c99818400ad481c982 languageName: node linkType: hard -"@vitest/expect@npm:0.31.1": - version: 0.31.1 - resolution: "@vitest/expect@npm:0.31.1" +"@vitest/expect@npm:0.32.2": + version: 0.32.2 + resolution: "@vitest/expect@npm:0.32.2" dependencies: - "@vitest/spy": "npm:0.31.1" - "@vitest/utils": "npm:0.31.1" + "@vitest/spy": "npm:0.32.2" + "@vitest/utils": "npm:0.32.2" chai: "npm:^4.3.7" - checksum: 428df2d5ed039eca6647c0e8709872db862aa6143dcc1968597612d0e2c3f16d28ec0f1f197ac4704e828b0d0e271754a8cc578ede4022d0248f4880ceb17a32 + checksum: 3ba00650af074d78b37fc4905b021e54ad46c87966400d01cc91aa4564928fee6b7bd772b2cee6e8b1bccdafd53566a291804a9f18f668d24d9780782e2dd02f languageName: node linkType: hard -"@vitest/runner@npm:0.31.1": - version: 0.31.1 - resolution: "@vitest/runner@npm:0.31.1" +"@vitest/runner@npm:0.32.2": + version: 0.32.2 + resolution: "@vitest/runner@npm:0.32.2" dependencies: - "@vitest/utils": "npm:0.31.1" + "@vitest/utils": "npm:0.32.2" concordance: "npm:^5.0.4" p-limit: "npm:^4.0.0" pathe: "npm:^1.1.0" - checksum: 2cf26cf503847b52f9389e658b7a7fa3f98139fe49a9eea774f25ae358c1f74ba1ecdc925488e1728cbdc9aaf0cf79ecdbd4c17f3a8363bd0797a9f6965e7c88 + checksum: 0087902b9f5676de8484167c82586040592a2b1ae8854b92b756b39431dd51e0a70789c3d6bf33795e6732fc7544355faaa156d31ee612fffcc021edcc588ca0 languageName: node linkType: hard -"@vitest/snapshot@npm:0.31.1": - version: 0.31.1 - resolution: "@vitest/snapshot@npm:0.31.1" +"@vitest/snapshot@npm:0.32.2": + version: 0.32.2 + resolution: "@vitest/snapshot@npm:0.32.2" dependencies: magic-string: "npm:^0.30.0" pathe: "npm:^1.1.0" pretty-format: "npm:^27.5.1" - checksum: 6d77b30f738c079a6b1f492f9a987d7d32851ae52935e17b492901a73f6ca05164531244e65a61fd34a8782caf338f003691f1d03e8ff30e354af202a18ee1fe + checksum: 7917ea3012eb61c329445081960b199e3af72a40b902e8e19d753d5f04ecca454d201f3ccd0669287c511b725ae3d21765de0ec00d561bab212abb4ebd157d0b languageName: node linkType: hard -"@vitest/spy@npm:0.31.1": - version: 0.31.1 - resolution: "@vitest/spy@npm:0.31.1" +"@vitest/spy@npm:0.32.2": + version: 0.32.2 + resolution: "@vitest/spy@npm:0.32.2" dependencies: tinyspy: "npm:^2.1.0" - checksum: 5d763585cff16f00851a1874bcb8da74f3f097e8e15ed688b972ff29673b07bbc9067e374df4817a7946218437cb3c6ec56b741ac9e58b5d91b9d68a826915d3 + checksum: df78e1620d53463115947828cc7a926ce6ae77e9ec3a97e7632a59ba96e04988d336e340ef60e3b8aa8e058ea5deb3bafe7f059cc253167deaa22d942fff5257 languageName: node linkType: hard -"@vitest/ui@npm:0.31.1": - version: 0.31.1 - resolution: "@vitest/ui@npm:0.31.1" +"@vitest/ui@npm:0.32.2": + version: 0.32.2 + resolution: "@vitest/ui@npm:0.32.2" dependencies: - "@vitest/utils": "npm:0.31.1" + "@vitest/utils": "npm:0.32.2" fast-glob: "npm:^3.2.12" fflate: "npm:^0.7.4" flatted: "npm:^3.2.7" @@ -2425,18 +2860,18 @@ __metadata: sirv: "npm:^2.0.3" peerDependencies: vitest: ">=0.30.1 <1" - checksum: f8186c07ea8f00832fe78cf5a3c2701471a9913f6ec0c0186f7af0b7be486c5ed146262bbb919ac086b36d51eb111471298f139948273c09a457849ade0e168c + checksum: c8ec26f83ffed65aa0457cb91b3cef75b9db55a845c65b68ce3017bd3921734013461b29fe04e31459794f86b579dc767bcbf1f674e9051bd8837923d8b3b7e0 languageName: node linkType: hard -"@vitest/utils@npm:0.31.1": - version: 0.31.1 - resolution: "@vitest/utils@npm:0.31.1" +"@vitest/utils@npm:0.32.2": + version: 0.32.2 + resolution: "@vitest/utils@npm:0.32.2" dependencies: - concordance: "npm:^5.0.4" + diff-sequences: "npm:^29.4.3" loupe: "npm:^2.3.6" pretty-format: "npm:^27.5.1" - checksum: a0294499ab6e493942d1e5edace996f346ec4a7dcd079764cde8c3aa85cd620ff5e5bc29a6fa9fee2ef638b65850815259115e94e31ca57a7c43221da35264a0 + checksum: 9214c3ff1c7a46d5b9fbe0c129bd55dc54b0f3b5e67899b6a8ffeeb7cb5790d1653b14e3220b23ee5a6b0b253e099f104aeb7ce0ae3422aebbb6af709c031fe5 languageName: node linkType: hard @@ -2447,6 +2882,13 @@ __metadata: languageName: node linkType: hard +"@whatwg-node/events@npm:^0.1.0": + version: 0.1.1 + resolution: "@whatwg-node/events@npm:0.1.1" + checksum: 89a2a79091d025b709559402e6606d1f7e9528d9a0fa00b69024fa6b31f0ba15eae0c24a08605558bd3469468a8e123839a5691d5bad55bf4c6407fcf0248673 + languageName: node + linkType: hard + "@whatwg-node/fetch@npm:0.6.2": version: 0.6.2 resolution: "@whatwg-node/fetch@npm:0.6.2" @@ -2477,6 +2919,16 @@ __metadata: languageName: node linkType: hard +"@whatwg-node/fetch@npm:^0.9.0": + version: 0.9.7 + resolution: "@whatwg-node/fetch@npm:0.9.7" + dependencies: + "@whatwg-node/node-fetch": "npm:^0.4.6" + urlpattern-polyfill: "npm:^9.0.0" + checksum: 95919f020e8722305358c5b4eb1a4bd55565cb83c4d5d37844379a076613189969df9ea45edbed74edf504eebd776a59be24e2224231850c09e959e7ea6c0071 + languageName: node + linkType: hard + "@whatwg-node/node-fetch@npm:0.0.1": version: 0.0.1 resolution: "@whatwg-node/node-fetch@npm:0.0.1" @@ -2490,6 +2942,19 @@ __metadata: languageName: node linkType: hard +"@whatwg-node/node-fetch@npm:^0.4.6": + version: 0.4.6 + resolution: "@whatwg-node/node-fetch@npm:0.4.6" + dependencies: + "@whatwg-node/events": "npm:^0.1.0" + busboy: "npm:^1.6.0" + fast-querystring: "npm:^1.1.1" + fast-url-parser: "npm:^1.1.3" + tslib: "npm:^2.3.1" + checksum: 7bb0cd28b3dbdb4b1f69a712038c1c1c04b41699076236c556c375254ae04588235fbef9e537a20fc7983ca1c9f6956917c76a261a79db6e599a888a3c1c1f87 + languageName: node + linkType: hard + "@zeit/schemas@npm:2.29.0": version: 2.29.0 resolution: "@zeit/schemas@npm:2.29.0" @@ -2661,6 +3126,15 @@ __metadata: languageName: node linkType: hard +"ansi-escapes@npm:^6.2.0": + version: 6.2.0 + resolution: "ansi-escapes@npm:6.2.0" + dependencies: + type-fest: "npm:^3.0.0" + checksum: 32ef889ff692e3082fae4e0cf5243487342cef80a621abb23967fc0c2fefdf2556c828d07e1cea04f24de2ca34b4c342a0a5a888d2ea493f8991aef057b47ff9 + languageName: node + linkType: hard + "ansi-regex@npm:^5.0.1": version: 5.0.1 resolution: "ansi-regex@npm:5.0.1" @@ -2707,6 +3181,13 @@ __metadata: languageName: node linkType: hard +"ansicolors@npm:~0.3.2": + version: 0.3.2 + resolution: "ansicolors@npm:0.3.2" + checksum: ec244826ff48ce81097507b8d273d05b94d3378d2a83cdcd1643b71a547dcc744023fa783a4aadf1a8fc98aa6fc63e27b6e9f8855bb344b1b447973b7b8d98ae + languageName: node + linkType: hard + "aproba@npm:^1.0.3 || ^2.0.0": version: 2.0.0 resolution: "aproba@npm:2.0.0" @@ -2924,6 +3405,15 @@ __metadata: languageName: node linkType: hard +"builtins@npm:^5.0.0": + version: 5.0.1 + resolution: "builtins@npm:5.0.1" + dependencies: + semver: "npm:^7.0.0" + checksum: d84d5abbe1480218e2f15c1179993047f291052614d6bc225359dc03932c6306002e94a3c86166a815478f1c9b2934a645dbfdee31c71ad50d71f128535d0c19 + languageName: node + linkType: hard + "busboy@npm:1.6.0, busboy@npm:^1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -3053,6 +3543,18 @@ __metadata: languageName: node linkType: hard +"cardinal@npm:^2.1.1": + version: 2.1.1 + resolution: "cardinal@npm:2.1.1" + dependencies: + ansicolors: "npm:~0.3.2" + redeyed: "npm:~2.1.0" + bin: + cdl: ./bin/cdl.js + checksum: 9e9716cde2c12612625cc1114d883c00ff19d511924772a5caad0646ef4fbf4912b52c8e35e990100ab3f08c7e8db81fce996b8407c198f7932e23ff7a79a829 + languageName: node + linkType: hard + "chai-each@npm:0.0.1": version: 0.0.1 resolution: "chai-each@npm:0.0.1" @@ -3260,6 +3762,19 @@ __metadata: languageName: node linkType: hard +"cli-table3@npm:^0.6.3": + version: 0.6.3 + resolution: "cli-table3@npm:0.6.3" + dependencies: + "@colors/colors": "npm:1.5.0" + string-width: "npm:^4.2.0" + dependenciesMeta: + "@colors/colors": + optional: true + checksum: 82fe6f515833019cdb7064c7276a546c5e3fe6bae6a1db4bf4b41e4bdcf9b119b086630f991461ac8556d82330ae5284fc4942a740118be6c8bbfcc69c118d0a + languageName: node + linkType: hard + "cli-truncate@npm:^2.1.0": version: 2.1.0 resolution: "cli-truncate@npm:2.1.0" @@ -3762,7 +4277,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:8.1.3, cosmiconfig@npm:^8.0.0": +"cosmiconfig@npm:8.1.3": version: 8.1.3 resolution: "cosmiconfig@npm:8.1.3" dependencies: @@ -3774,6 +4289,18 @@ __metadata: languageName: node linkType: hard +"cosmiconfig@npm:^8.0.0, cosmiconfig@npm:^8.1.0": + version: 8.2.0 + resolution: "cosmiconfig@npm:8.2.0" + dependencies: + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.0" + parse-json: "npm:^5.0.0" + path-type: "npm:^4.0.0" + checksum: 25a8d33a2372b8dd02993bf8c7a6e9e0d7d70692dc7e00da3848f3ff3e4bac1ab734d36d08dba2e10a49b69c316ca6fb92f9b7fc3eed60b7e65a60609941eccd + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -3950,6 +4477,13 @@ __metadata: languageName: node linkType: hard +"dataloader@npm:^2.2.2": + version: 2.2.2 + resolution: "dataloader@npm:2.2.2" + checksum: 73ca3126c7828273b50e982ede9ac7f1bc7ec1df46c91863600ab27889ad508f5ee4ea62ce5fe8c22a598a4066bb47db6e785d3db9c8b48466993dce7e81f4e4 + languageName: node + linkType: hard + "date-time@npm:^3.1.0": version: 3.1.0 resolution: "date-time@npm:3.1.0" @@ -4076,6 +4610,13 @@ __metadata: languageName: node linkType: hard +"diff-sequences@npm:^29.4.3": + version: 29.4.3 + resolution: "diff-sequences@npm:29.4.3" + checksum: 788bca9220b2c7453bed921045660717c0ffb4ba9ca1456417e6e32d67e21fcebc62b37c0291f8e32177aa7b30913dd2fe240dfb4872cfcd7a09b738f8f120d5 + languageName: node + linkType: hard + "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -4128,7 +4669,7 @@ __metadata: languageName: node linkType: hard -"dset@npm:3.1.2": +"dset@npm:3.1.2, dset@npm:^3.1.2": version: 3.1.2 resolution: "dset@npm:3.1.2" checksum: f81e27f95bed3e766e7521ee45d908bc235632dbfeab62eba9f185eb27d5610b4e6873605b04d39fd9bcbd5625d3064b3e2aa2b8470a19af46343abb535010c5 @@ -4204,7 +4745,84 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:0.17.19, esbuild@npm:^0.17.5": +"esbuild@npm:0.18.6": + version: 0.18.6 + resolution: "esbuild@npm:0.18.6" + dependencies: + "@esbuild/android-arm": "npm:0.18.6" + "@esbuild/android-arm64": "npm:0.18.6" + "@esbuild/android-x64": "npm:0.18.6" + "@esbuild/darwin-arm64": "npm:0.18.6" + "@esbuild/darwin-x64": "npm:0.18.6" + "@esbuild/freebsd-arm64": "npm:0.18.6" + "@esbuild/freebsd-x64": "npm:0.18.6" + "@esbuild/linux-arm": "npm:0.18.6" + "@esbuild/linux-arm64": "npm:0.18.6" + "@esbuild/linux-ia32": "npm:0.18.6" + "@esbuild/linux-loong64": "npm:0.18.6" + "@esbuild/linux-mips64el": "npm:0.18.6" + "@esbuild/linux-ppc64": "npm:0.18.6" + "@esbuild/linux-riscv64": "npm:0.18.6" + "@esbuild/linux-s390x": "npm:0.18.6" + "@esbuild/linux-x64": "npm:0.18.6" + "@esbuild/netbsd-x64": "npm:0.18.6" + "@esbuild/openbsd-x64": "npm:0.18.6" + "@esbuild/sunos-x64": "npm:0.18.6" + "@esbuild/win32-arm64": "npm:0.18.6" + "@esbuild/win32-ia32": "npm:0.18.6" + "@esbuild/win32-x64": "npm:0.18.6" + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: c23b434c6e5973deaa3da3e3cf994d7babba8f4ddf9203552b08b19a8df54c97f7ff44601be433986f2c682b151d8bb93efefd73f4fac9d2ebeec96086f7a561 + languageName: node + linkType: hard + +"esbuild@npm:^0.17.5": version: 0.17.19 resolution: "esbuild@npm:0.17.19" dependencies: @@ -4375,16 +4993,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jsonc@npm:2.8.0": - version: 2.8.0 - resolution: "eslint-plugin-jsonc@npm:2.8.0" +"eslint-plugin-jsonc@npm:2.9.0": + version: 2.9.0 + resolution: "eslint-plugin-jsonc@npm:2.9.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" jsonc-eslint-parser: "npm:^2.0.4" natural-compare: "npm:^1.4.0" peerDependencies: eslint: ">=6.0.0" - checksum: 1d2d15911d5a19d84d999ab93c234c74670970f0db754fb320e79db765726f5f9a87b0154f87c85834d18d382fa7725e73d6d4575bbd4a68cc7e71b3d1f719a7 + checksum: 1e2c92d829597b9073101e750ac6bc45e72a2ca0491cd0b04db9662d53e8e8a6d4a0546d53ff776a9fb55e5930f8a242686677f160b494658459b3c9199962aa languageName: node linkType: hard @@ -4476,9 +5094,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-yml@npm:1.7.0": - version: 1.7.0 - resolution: "eslint-plugin-yml@npm:1.7.0" +"eslint-plugin-yml@npm:1.8.0": + version: 1.8.0 + resolution: "eslint-plugin-yml@npm:1.8.0" dependencies: debug: "npm:^4.3.2" lodash: "npm:^4.17.21" @@ -4486,7 +5104,7 @@ __metadata: yaml-eslint-parser: "npm:^1.2.1" peerDependencies: eslint: ">=6.0.0" - checksum: 8fcfcde833750f252e9847b89c58bca7151318c1921a88a494f7a6b7f8afac7f672ed32063dad406122e9f858eb1ce5f1a8c55c375cbfdf1ba335c53e7f79e4e + checksum: 05de5fb6e56f65d8d07f4531383a41430e45328407dabc21810f87b6e093483482563c9335022b646bb418d61cd46217591c8405e2e6200d9e61aa9793fd9c95 languageName: node linkType: hard @@ -4533,15 +5151,15 @@ __metadata: languageName: node linkType: hard -"eslint@npm:8.41.0": - version: 8.41.0 - resolution: "eslint@npm:8.41.0" +"eslint@npm:8.43.0": + version: 8.43.0 + resolution: "eslint@npm:8.43.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.4.0" "@eslint/eslintrc": "npm:^2.0.3" - "@eslint/js": "npm:8.41.0" - "@humanwhocodes/config-array": "npm:^0.11.8" + "@eslint/js": "npm:8.43.0" + "@humanwhocodes/config-array": "npm:^0.11.10" "@humanwhocodes/module-importer": "npm:^1.0.1" "@nodelib/fs.walk": "npm:^1.2.8" ajv: "npm:^6.10.0" @@ -4578,7 +5196,7 @@ __metadata: text-table: "npm:^0.2.0" bin: eslint: bin/eslint.js - checksum: 82a4a6fd28d88b15e2c8769d291449b93a2241934883d15236eccddce7154f60bf84657923a4840d62988ab099118b71b4ffa1bb7e3039bf1c69aad2ba15f236 + checksum: 9f7e1cfdbe5219cdcecbf412c4535d68b42d777aa5a1524c565ac50ae192880f8f1ca898552e41d649e1b04e2e64feee33879740c5ab5015709eef529b70e876 languageName: node linkType: hard @@ -4593,7 +5211,7 @@ __metadata: languageName: node linkType: hard -"esprima@npm:^4.0.1": +"esprima@npm:^4.0.1, esprima@npm:~4.0.0": version: 4.0.1 resolution: "esprima@npm:4.0.1" bin: @@ -4725,6 +5343,13 @@ __metadata: languageName: node linkType: hard +"fast-decode-uri-component@npm:^1.0.1": + version: 1.0.1 + resolution: "fast-decode-uri-component@npm:1.0.1" + checksum: 47a0e7876b61e6f2661d43abd50cbb1cbb1a3e4eaadc428fbb914a5c78ad1a187f9fc1e4570052eb18d02239fbfd66642cd7b1adee5e927821fba0c93e7096db + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -4780,7 +5405,16 @@ __metadata: languageName: node linkType: hard -"fast-url-parser@npm:1.1.3": +"fast-querystring@npm:^1.1.1": + version: 1.1.2 + resolution: "fast-querystring@npm:1.1.2" + dependencies: + fast-decode-uri-component: "npm:^1.0.1" + checksum: fc70deb15d019b8c56dbaa692c7c45cb73d223e469297d8d05779e3051b82ee9810701e1ea81abaab1396a6bc387237d47267092af63f85045c15b72bcfedf0e + languageName: node + linkType: hard + +"fast-url-parser@npm:1.1.3, fast-url-parser@npm:^1.1.3": version: 1.1.3 resolution: "fast-url-parser@npm:1.1.3" dependencies: @@ -4798,6 +5432,15 @@ __metadata: languageName: node linkType: hard +"fetch-ponyfill@npm:^7.1.0": + version: 7.1.0 + resolution: "fetch-ponyfill@npm:7.1.0" + dependencies: + node-fetch: "npm:~2.6.1" + checksum: 7e5ebfeef1ebfbebf3264a828e27fd88c97c52183108d8d70235f37503929351532556295f5030587b8f5f258c6c6869eb03e002c30d2daccaaeb3fd71b9b514 + languageName: node + linkType: hard + "fflate@npm:^0.7.4": version: 0.7.4 resolution: "fflate@npm:0.7.4" @@ -5230,7 +5873,32 @@ __metadata: languageName: node linkType: hard -"graphql-config@npm:4.5.0, graphql-config@npm:^4.4.0": +"graphql-config@npm:5.0.2": + version: 5.0.2 + resolution: "graphql-config@npm:5.0.2" + dependencies: + "@graphql-tools/graphql-file-loader": "npm:^8.0.0" + "@graphql-tools/json-file-loader": "npm:^8.0.0" + "@graphql-tools/load": "npm:^8.0.0" + "@graphql-tools/merge": "npm:^9.0.0" + "@graphql-tools/url-loader": "npm:^8.0.0" + "@graphql-tools/utils": "npm:^10.0.0" + cosmiconfig: "npm:^8.1.0" + jiti: "npm:^1.18.2" + minimatch: "npm:^4.2.3" + string-env-interpolation: "npm:^1.0.1" + tslib: "npm:^2.4.0" + peerDependencies: + cosmiconfig-toml-loader: ^1.0.0 + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + cosmiconfig-toml-loader: + optional: true + checksum: 5ef7794ab6b202725344344d4e287548403c17d9efc8bd8e0f5fad6ff0e83fa1c9c5f0b7283a8f006261dc7e46d2393cd4f99c4e7dc6f1cb15b4c773cee93b60 + languageName: node + linkType: hard + +"graphql-config@npm:^4.4.0": version: 4.5.0 resolution: "graphql-config@npm:4.5.0" dependencies: @@ -5275,10 +5943,19 @@ __metadata: languageName: node linkType: hard -"graphql@npm:16.6.0": - version: 16.6.0 - resolution: "graphql@npm:16.6.0" - checksum: 686582916b9ca247f3562f086c34a6363155475da909e1f891f9a76a3b5273ed6c7034cd5a82e768670ac5a74d539a6f1be282253a92b49be3a489fc82e83a5b +"graphql-ws@npm:5.14.0": + version: 5.14.0 + resolution: "graphql-ws@npm:5.14.0" + peerDependencies: + graphql: ">=0.11 <=16" + checksum: 07c51da2df023620a2cf44b6a86ad28aa32a0b85975d46d8c99476a78944ce480245e874345b6068cc207e8a029dec2f0d2a1d9b19c4c8cd69819314cb3c4828 + languageName: node + linkType: hard + +"graphql@npm:16.7.0": + version: 16.7.0 + resolution: "graphql@npm:16.7.0" + checksum: 5a723f99e9995ab357b7bd8ae6e184267a1fd8cefc271a9187de83a330d98334f8a9197352664754054bdf23d29c1fb939af7d2940288312b39bd2449adc0464 languageName: node linkType: hard @@ -5806,7 +6483,7 @@ __metadata: languageName: node linkType: hard -"isomorphic-ws@npm:5.0.0": +"isomorphic-ws@npm:5.0.0, isomorphic-ws@npm:^5.0.0": version: 5.0.0 resolution: "isomorphic-ws@npm:5.0.0" peerDependencies: @@ -5852,6 +6529,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^1.18.2": + version: 1.18.2 + resolution: "jiti@npm:1.18.2" + bin: + jiti: bin/jiti.js + checksum: d2204dd32c4012a3f801cb5538da56673308363ec7ca84974d1ccf37c565c67db421688ebd6d70a8ed274b26a8a205176f9e53699b43c6c2d2c3d7c4284d657d + languageName: node + linkType: hard + "js-string-escape@npm:^1.0.1": version: 1.0.1 resolution: "js-string-escape@npm:1.0.1" @@ -6357,6 +7043,31 @@ __metadata: languageName: node linkType: hard +"marked-terminal@npm:^5.2.0": + version: 5.2.0 + resolution: "marked-terminal@npm:5.2.0" + dependencies: + ansi-escapes: "npm:^6.2.0" + cardinal: "npm:^2.1.1" + chalk: "npm:^5.2.0" + cli-table3: "npm:^0.6.3" + node-emoji: "npm:^1.11.0" + supports-hyperlinks: "npm:^2.3.0" + peerDependencies: + marked: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + checksum: 5f99ad8743745372ea80eae9711dc84ee1a0489182417403d0ea1c0123bac9f65da05fe1b756630d8c47a26c32741691fe00bdb1820ff50d74e29b051152d209 + languageName: node + linkType: hard + +"marked@npm:^5.1.0": + version: 5.1.0 + resolution: "marked@npm:5.1.0" + bin: + marked: bin/marked.js + checksum: 017f42f088b33ef7d595333e39566a19fa850a6fbea25bf5d3d9d88bfbb64659d29c09a8d6ca6847d2907a16cbca6626662336fcb5a40e40fa7eb79aaedf7a42 + languageName: node + linkType: hard + "md5-hex@npm:^3.0.1": version: 3.0.1 resolution: "md5-hex@npm:3.0.1" @@ -6467,6 +7178,18 @@ __metadata: languageName: node linkType: hard +"meros@npm:^1.2.1": + version: 1.3.0 + resolution: "meros@npm:1.3.0" + peerDependencies: + "@types/node": ">=13" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 1fd944338a309b42ab9e33204d7a13ea2f47b0951d413937f864ddac0fb30b5f0895e3901af078b5602cf3c623d6fb5b789cbb43aece9e27d51025a95defaafa + languageName: node + linkType: hard + "micromark@npm:~2.11.0": version: 2.11.4 resolution: "micromark@npm:2.11.4" @@ -6549,7 +7272,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:4.2.3": +"minimatch@npm:4.2.3, minimatch@npm:^4.2.3": version: 4.2.3 resolution: "minimatch@npm:4.2.3" dependencies: @@ -6803,9 +7526,18 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.9": - version: 2.6.9 - resolution: "node-fetch@npm:2.6.9" +"node-emoji@npm:^1.11.0": + version: 1.11.0 + resolution: "node-emoji@npm:1.11.0" + dependencies: + lodash: "npm:^4.17.21" + checksum: d94fcc48d9c3dc1f2512bf525f5c614d0b88c9c711c7d116f06ec8adc6d25082959c1c6a37fe9ae431ba4018018ca13bed256f94e61c347e4618b1276b841d3c + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.9, node-fetch@npm:~2.6.1": + version: 2.6.12 + resolution: "node-fetch@npm:2.6.12" dependencies: whatwg-url: "npm:^5.0.0" peerDependencies: @@ -6813,7 +7545,7 @@ __metadata: peerDependenciesMeta: encoding: optional: true - checksum: 8457cf62f599e9d55b01d58f87ed2110c65f83c4fcce8be0e350909995384e96a55e2b810d0e1a67a1fbe7f9930cd0998146d2dcce4843f9ed3ac0b479bd5c64 + checksum: 9db93926c26f46e727034c25a9b648a5345840052ab7fcba1760353929e99eba0225bd196aa3ed57600a98bba4722a81ea243417a9e740d8c440ff1f5b0139b4 languageName: node linkType: hard @@ -7700,6 +8432,15 @@ __metadata: languageName: node linkType: hard +"redeyed@npm:~2.1.0": + version: 2.1.1 + resolution: "redeyed@npm:2.1.1" + dependencies: + esprima: "npm:~4.0.0" + checksum: 1278b73beed9164dc01f9f1b83349e6492b2fd5811bc58d644f6850d526135eb3dce08e9d468fa6f1475b1428a732da003dc8b5972b9eca218dcdc75526b6b77 + languageName: node + linkType: hard + "regexp-tree@npm:^0.1.24, regexp-tree@npm:~0.1.1": version: 0.1.24 resolution: "regexp-tree@npm:0.1.24" @@ -7961,25 +8702,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.5.0": - version: 7.5.0 - resolution: "semver@npm:7.5.0" - dependencies: - lru-cache: "npm:^6.0.0" - bin: - semver: bin/semver.js - checksum: 4cc7856258319d36c441fb969fa2ee622fa3761ce1b04013ed5d05979adfcf3079f4fc4dff11f9b2588802620d579da2c209a4e26803d9adfbf44b78244b683b - languageName: node - linkType: hard - -"semver@npm:7.5.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.0": - version: 7.5.1 - resolution: "semver@npm:7.5.1" +"semver@npm:7.5.2": + version: 7.5.2 + resolution: "semver@npm:7.5.2" dependencies: lru-cache: "npm:^6.0.0" bin: semver: bin/semver.js - checksum: 20fce7894334634f553a64334b21fa2dc7cccd08a5f31122c187f1c1d3e45b8fc47d599d3fa52342c3fb3ee0bd139a8fbd41bc5b9e849ada8f7e7cc91b18a180 + checksum: 896fab94563f3e4187b57a883e2efab277488bf4bd823d7794410435c39a1191830ae366470baad3430f74a555534d06cfc85c3e1b82f2bdeb9dd34edc980189 languageName: node linkType: hard @@ -7992,6 +8722,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.0.0, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.0": + version: 7.5.3 + resolution: "semver@npm:7.5.3" + dependencies: + lru-cache: "npm:^6.0.0" + bin: + semver: bin/semver.js + checksum: 9e949f7d5797373fcd8986b9b667cbd45afd4e95619ef82978d158a50c47579224e8f39badf03f6b9e8f4c80a1c87d6415eb1ee8b476e7bb60e6c7e8f63279be + languageName: node + linkType: hard + "serve-handler@npm:6.1.5": version: 6.1.5 resolution: "serve-handler@npm:6.1.5" @@ -8279,7 +9020,7 @@ __metadata: languageName: node linkType: hard -"string-env-interpolation@npm:1.0.1": +"string-env-interpolation@npm:1.0.1, string-env-interpolation@npm:^1.0.1": version: 1.0.1 resolution: "string-env-interpolation@npm:1.0.1" checksum: 38c321857f2911ad2bfce842300cc7bf700191779875c458739531e06ada8e9406e3efd08c74780e515eff3239b7ad1f8b99a1a3765edb64b70ec4659d8c2a5f @@ -8429,7 +9170,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^7.1.0": +"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" dependencies: @@ -8438,6 +9179,16 @@ __metadata: languageName: node linkType: hard +"supports-hyperlinks@npm:^2.3.0": + version: 2.3.0 + resolution: "supports-hyperlinks@npm:2.3.0" + dependencies: + has-flag: "npm:^4.0.0" + supports-color: "npm:^7.0.0" + checksum: 018edbc2b3c5c1bea3b525dfc0b4fe8a3ab21cb61cd5c4b23aee11da540b81e8ff8bb022fa8eae3c87c4779533a5b4b763f31da1f76bffc27613c9b15a863a13 + languageName: node + linkType: hard + "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -8811,6 +9562,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^3.0.0": + version: 3.12.0 + resolution: "type-fest@npm:3.12.0" + checksum: 4272f157b6dcc98151e2a0bcb97f5b271115515528aa92046d1cd91699830b54f5e86f4bf55ff4505471c1434630b71f4fd1ecab0b8b2b8dfb3a05869bdf4272 + languageName: node + linkType: hard + "typedarray-to-buffer@npm:^3.1.5": version: 3.1.5 resolution: "typedarray-to-buffer@npm:3.1.5" @@ -8827,43 +9585,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.1.0-beta": - version: 5.1.0-beta - resolution: "typescript@npm:5.1.0-beta" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: b48af60cecab067787f1743205538c07f36d489719f2240c314c736f7a9e7afa07929e38dea75bfc02467d64dd77f5eaeb465b8bff17c21805f52442b60eb8ff - languageName: node - linkType: hard - -"typescript@npm:^4.6.4 || ^5.0.0": - version: 5.0.4 - resolution: "typescript@npm:5.0.4" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 56649de784c427e8f3f63d4ebfcada4fcf03bb2a301f3327342111798db7f26f8a86285f979f376cf6cec4774bd96b4650f2693a07fc409f4544ad4c4d9fe4c9 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A5.1.0-beta#optional!builtin": - version: 5.1.0-beta - resolution: "typescript@patch:typescript@npm%3A5.1.0-beta#optional!builtin::version=5.1.0-beta&hash=1f5320" +"typescript@npm:5.1.6, typescript@npm:^4.6.4 || ^5.0.0, typescript@npm:^5.1.3": + version: 5.1.6 + resolution: "typescript@npm:5.1.6" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 68d5f488b36d87e62fdc5724cf7044f2ef58f621153c01d5f9041992497a09ad50454b44afef7341039733705f37e50a3f22334a63bc0d7c0132fb5712c4a916 + checksum: fffaefc0d48e7f505ac42e314628b3cd41b2a69428e0ecc158db84af36a0565d4cfdd337ea8f7933cccf1556b08170e4389ee07974ff9f55f86d01342b1951ec languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^4.6.4 || ^5.0.0#optional!builtin": - version: 5.0.4 - resolution: "typescript@patch:typescript@npm%3A5.0.4#optional!builtin::version=5.0.4&hash=1f5320" +"typescript@patch:typescript@npm%3A5.1.6#optional!builtin, typescript@patch:typescript@npm%3A^4.6.4 || ^5.0.0#optional!builtin, typescript@patch:typescript@npm%3A^5.1.3#optional!builtin": + version: 5.1.6 + resolution: "typescript@patch:typescript@npm%3A5.1.6#optional!builtin::version=5.1.6&hash=1f5320" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: be94f10d96a8443e5524bce56705712ae1aa6c67618dd2c316ddb616ea614a3eb675c34e4af16ab09f1a0ab694173c704ba5b23d8b8bda31cb31c1ca078c486f + checksum: 3eef948e10c71b96172ebce471c031d3b7afb12a48feeacbadac1876c0b4e9b0c0fc1677bef53d41664970a174a9ee02f9d09be260281571c90e8ef4e22574cf languageName: node linkType: hard @@ -8986,6 +9724,13 @@ __metadata: languageName: node linkType: hard +"urlpattern-polyfill@npm:^9.0.0": + version: 9.0.0 + resolution: "urlpattern-polyfill@npm:9.0.0" + checksum: 9c86e08fa693219127edf42276b72fac5591b01e3b1093acba34f6add0a783a985555761761a93f539b2c7f452ea4b28e63d036c05225e1618d0ca2f2b052779 + languageName: node + linkType: hard + "user-home@npm:^2.0.0": version: 2.0.0 resolution: "user-home@npm:2.0.0" @@ -9039,7 +9784,16 @@ __metadata: languageName: node linkType: hard -"value-or-promise@npm:1.0.12, value-or-promise@npm:^1.0.11": +"validate-npm-package-name@npm:^5.0.0": + version: 5.0.0 + resolution: "validate-npm-package-name@npm:5.0.0" + dependencies: + builtins: "npm:^5.0.0" + checksum: 18d5883d8bd10fa56fdeee755802f19b8b769313a85892b7291e50d8bdbb237b077d1ab7a0ae9612666719baa9fb5d1daf36e0b7ff318b0b2a61a54fba122a49 + languageName: node + linkType: hard + +"value-or-promise@npm:1.0.12, value-or-promise@npm:^1.0.11, value-or-promise@npm:^1.0.12": version: 1.0.12 resolution: "value-or-promise@npm:1.0.12" checksum: c516b23601ae3f14ab38693dc02df5ebf3c4ef74f5050fb8d3a7349146894dabb3783f051e0471110e81e842e80b464f44644ded9d1b4a0df3e6e37c6e166bff @@ -9064,9 +9818,9 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:0.31.1": - version: 0.31.1 - resolution: "vite-node@npm:0.31.1" +"vite-node@npm:0.32.2": + version: 0.32.2 + resolution: "vite-node@npm:0.32.2" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.3.4" @@ -9076,7 +9830,7 @@ __metadata: vite: "npm:^3.0.0 || ^4.0.0" bin: vite-node: vite-node.mjs - checksum: b1266c658a2c6f105d9813d7d65ac6f083b61a50257658b3e4c7e4bdf418255b1cabbbe815a37c7f889e7ba0bd90b7214cffc69063cd64e8b646e2799e97916e + checksum: f4551a79bb606c1139aece5b4efa974fd84ad02fa11c76f27aaf5cfe418cd1c7b1d148569ed357b063c5413acbfe36fae6415ea75750c7f08bc1dc08ed3f072b languageName: node linkType: hard @@ -9096,9 +9850,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:4.3.8, vite@npm:^3.0.0 || ^4.0.0": - version: 4.3.8 - resolution: "vite@npm:4.3.8" +"vite@npm:4.3.9, vite@npm:^3.0.0 || ^4.0.0": + version: 4.3.9 + resolution: "vite@npm:4.3.9" dependencies: esbuild: "npm:^0.17.5" fsevents: "npm:~2.3.2" @@ -9129,7 +9883,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: f702b12edab33c96133f03ac5051f81d429fcc70f2d784a6ad710978b696070e6ae6c832326fb733b74361dfc4b7f6f696b2c04628e594d5999cfd74c3f72166 + checksum: 3bdd4d860e646a5abdf9b0af1828d74e7e20f9124cc4081cc7d8a09df1b054286add51051c1127cc3c1ea522e3c756da21a1dc424dabd121fa5b82dd5c6911b1 languageName: node linkType: hard @@ -9144,18 +9898,79 @@ __metadata: languageName: node linkType: hard -"vitest@npm:0.31.1": - version: 0.31.1 - resolution: "vitest@npm:0.31.1" +"vitest@npm:0.32.2": + version: 0.32.2 + resolution: "vitest@npm:0.32.2" + dependencies: + "@types/chai": "npm:^4.3.5" + "@types/chai-subset": "npm:^1.3.3" + "@types/node": "npm:*" + "@vitest/expect": "npm:0.32.2" + "@vitest/runner": "npm:0.32.2" + "@vitest/snapshot": "npm:0.32.2" + "@vitest/spy": "npm:0.32.2" + "@vitest/utils": "npm:0.32.2" + acorn: "npm:^8.8.2" + acorn-walk: "npm:^8.2.0" + cac: "npm:^6.7.14" + chai: "npm:^4.3.7" + concordance: "npm:^5.0.4" + debug: "npm:^4.3.4" + local-pkg: "npm:^0.4.3" + magic-string: "npm:^0.30.0" + pathe: "npm:^1.1.0" + picocolors: "npm:^1.0.0" + std-env: "npm:^3.3.2" + strip-literal: "npm:^1.0.1" + tinybench: "npm:^2.5.0" + tinypool: "npm:^0.5.0" + vite: "npm:^3.0.0 || ^4.0.0" + vite-node: "npm:0.32.2" + why-is-node-running: "npm:^2.2.2" + peerDependencies: + "@edge-runtime/vm": "*" + "@vitest/browser": "*" + "@vitest/ui": "*" + happy-dom: "*" + jsdom: "*" + playwright: "*" + safaridriver: "*" + webdriverio: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + bin: + vitest: vitest.mjs + checksum: fb2cbf14ece00e2ec24d4d7952cd5e628e75ad17bedae25f06c1dfb904121d6aece41bfc169ae489e5d6e9c92088d237cf5e4a4ad1db93bb44ea76e445d58ffb + languageName: node + linkType: hard + +"vitest@patch:vitest@npm%3A0.32.2#patches/vitest+0.32.2.dev.patch::locator=%40flex-development%2Ftutils%40workspace%3A.": + version: 0.32.2 + resolution: "vitest@patch:vitest@npm%3A0.32.2#patches/vitest+0.32.2.dev.patch::version=0.32.2&hash=e3d1ef&locator=%40flex-development%2Ftutils%40workspace%3A." dependencies: "@types/chai": "npm:^4.3.5" "@types/chai-subset": "npm:^1.3.3" "@types/node": "npm:*" - "@vitest/expect": "npm:0.31.1" - "@vitest/runner": "npm:0.31.1" - "@vitest/snapshot": "npm:0.31.1" - "@vitest/spy": "npm:0.31.1" - "@vitest/utils": "npm:0.31.1" + "@vitest/expect": "npm:0.32.2" + "@vitest/runner": "npm:0.32.2" + "@vitest/snapshot": "npm:0.32.2" + "@vitest/spy": "npm:0.32.2" + "@vitest/utils": "npm:0.32.2" acorn: "npm:^8.8.2" acorn-walk: "npm:^8.2.0" cac: "npm:^6.7.14" @@ -9171,7 +9986,7 @@ __metadata: tinybench: "npm:^2.5.0" tinypool: "npm:^0.5.0" vite: "npm:^3.0.0 || ^4.0.0" - vite-node: "npm:0.31.1" + vite-node: "npm:0.32.2" why-is-node-running: "npm:^2.2.2" peerDependencies: "@edge-runtime/vm": "*" @@ -9201,7 +10016,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 008fee1e051857c724b0c252afc957cdcc1321c3d782034ce31763f529a44af6868923e0264ea9697ce00e2ac74353fa9d44ee2979da038d6efd5a9b6cf0524d + checksum: f66cebe949f55788bc6a084b91a4006cc4b6ce166dd5ad5509231abc76c2ef87676c009289065a1937898410b5649cdebb18fe2d571ae19c2571afdd3e1ca480 languageName: node linkType: hard @@ -9392,6 +10207,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.13.0, ws@npm:^8.12.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: af5cfb5a7031d1183e3c33d9ea917b2f36b127aac3ecd6a7890927fed583aa65b464242f2bd570ad83114ffefc21daf442d02a23fb9bc93a8c6a199febbd9304 + languageName: node + linkType: hard + "xdg-basedir@npm:^4.0.0": version: 4.0.0 resolution: "xdg-basedir@npm:4.0.0"