├── .changeset ├── README.md ├── blue-pumpkins-accept.md ├── bright-ghosts-count.md ├── calm-cats-beam.md ├── chilly-rules-applaud.md ├── config.json ├── dirty-stingrays-greet.md ├── dull-owls-trade.md ├── fast-dolls-design.md ├── fast-peaches-occur.md ├── fluffy-files-flash.md ├── fresh-spiders-invite.md ├── gentle-papayas-beg.md ├── good-cups-approve.md ├── good-squids-yawn.md ├── grumpy-bananas-jog.md ├── itchy-cups-complain.md ├── itchy-mails-sleep.md ├── itchy-planes-remember.md ├── khaki-queens-lay.md ├── khaki-yaks-unite.md ├── large-windows-obey.md ├── late-seals-lick.md ├── little-donkeys-pay.md ├── lovely-horses-press.md ├── lucky-socks-walk.md ├── metal-buckets-stare.md ├── mighty-pumas-wink.md ├── moody-badgers-refuse.md ├── nice-forks-hide.md ├── ninety-dolphins-refuse.md ├── odd-panthers-jog.md ├── polite-games-guess.md ├── pre.json ├── purple-countries-decide.md ├── quick-grapes-travel.md ├── red-plums-look.md ├── rich-spies-sneeze.md ├── selfish-sheep-explain.md ├── serious-deers-cheer.md ├── serious-suns-drive.md ├── seven-peaches-explode.md ├── shaggy-roses-hug.md ├── sharp-walls-deliver.md ├── short-suns-stare.md ├── silly-hornets-train.md ├── silver-eggs-add.md ├── sixty-ears-fry.md ├── strong-months-behave.md ├── strong-rabbits-tie.md ├── sweet-tools-clap.md ├── tasty-numbers-chew.md ├── three-pants-hunt.md ├── tidy-coins-relate.md ├── tough-files-sort.md ├── tough-forks-dance.md ├── tricky-wasps-glow.md ├── twelve-shirts-lay.md ├── two-shrimps-wonder.md └── wise-zoos-jog.md ├── .config └── typedoc.jsonc ├── .editorconfig ├── .gitattributes ├── .github ├── codeql │ └── codeql-config.yml ├── contributing.md ├── mergify.yml ├── renovate.json └── workflows │ ├── codeql-analysis.yml │ ├── pull-request.yml │ └── release.yml ├── .gitignore ├── .husky └── commit-msg ├── .markdownlint-cli2.jsonc ├── .prettierignore ├── .releaserc.json ├── .vscode ├── extensions.json ├── launch.json ├── ltex.dictionary.en-US.txt ├── ltex.hiddenFalsePositives.en-US.txt ├── markdown-snippets.code-snippets ├── settings.json ├── type-plus.code-workspace └── typescript.code-snippets ├── LICENSE ├── apps └── website │ ├── .gitignore │ ├── README.md │ ├── astro.config.mjs │ ├── package.json │ ├── public │ ├── favicon.svg │ └── type-plus.svg │ ├── src │ ├── assets │ │ └── houston.webp │ ├── content.config.ts │ └── content │ │ └── docs │ │ ├── api │ │ ├── primitives.mdx │ │ └── type-branching.mdx │ │ ├── guides │ │ ├── example.md │ │ ├── getting-started.mdx │ │ └── typescript-version-compatibility.mdx │ │ ├── index.mdx │ │ └── reference │ │ ├── categories.md │ │ ├── options.md │ │ └── status.md │ └── tsconfig.json ├── biome.json ├── commitlint.config.cjs ├── eslint.config.mjs ├── flow-types.md ├── info.md ├── jest.config.mjs ├── old └── checker │ ├── package.json │ ├── src │ ├── type-checker │ │ ├── .eslintrc.json │ │ ├── AnyType.ts │ │ ├── Boolean.ts │ │ ├── Tuple.spec.ts │ │ ├── Tuple.ts │ │ ├── buildTypes.spec.ts │ │ ├── index.ts │ │ ├── typeChecker.spec.ts │ │ ├── typeChecker.ts │ │ └── types.ts │ └── types │ │ ├── .eslintrc.json │ │ ├── AllType.ts │ │ ├── Any.ts │ │ ├── Array.ts │ │ ├── BigInt.ts │ │ ├── Boolean.ts │ │ ├── Generate.ts │ │ ├── Null.ts │ │ ├── Number.ts │ │ ├── Object.ts │ │ ├── Record.ts │ │ ├── String.ts │ │ ├── Symbol.ts │ │ ├── Tuple.ts │ │ ├── Undefined.ts │ │ ├── Union.ts │ │ ├── Unknown.ts │ │ ├── analyze.spec.ts │ │ ├── analyze.ts │ │ ├── check.spec.ts │ │ ├── check.ts │ │ ├── conform.spec.ts │ │ ├── conform.ts │ │ ├── getPlainAnalysisReport.spec.ts │ │ ├── getPlainAnalysisReport.ts │ │ ├── index.ts │ │ ├── optional.ts │ │ ├── required.ts │ │ ├── satisfy.accept.ts │ │ ├── satisfy.spec.ts │ │ ├── satisfy.ts │ │ └── types.ts │ └── tsconfig.json ├── package.json ├── packages ├── kind │ ├── .depcheckrc.yaml │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── fn.spec.ts │ │ ├── fn.ts │ │ ├── id.ts │ │ └── index.ts │ └── tsconfig.json └── type-plus │ ├── .depcheckrc.yaml │ ├── .gitignore │ ├── .repobuddy.json │ ├── .size-limit.json │ ├── CHANGELOG.md │ ├── LICENSE │ ├── fixtures │ └── eslint │ │ └── cases │ │ └── empty.json │ ├── jest.config.mjs │ ├── package.json │ ├── readme.md │ ├── src │ ├── $type │ │ ├── $resolve_options.spec.ts │ │ ├── $resolve_options.ts │ │ ├── $type.spec.ts │ │ ├── $type.ts │ │ ├── branch │ │ │ ├── $branch.spec.ts │ │ │ ├── $branch.ts │ │ │ ├── $input_options.spec.ts │ │ │ ├── $input_options.ts │ │ │ ├── $resolve_branch.spec.ts │ │ │ ├── $resolve_branch.ts │ │ │ ├── $selection.ts │ │ │ └── readme.md │ │ ├── distributive │ │ │ ├── $distributive.spec.ts │ │ │ ├── $distributive.ts │ │ │ └── readme.md │ │ ├── errors │ │ │ ├── $error.spec.ts │ │ │ ├── $error.ts │ │ │ ├── $infer_error.spec.ts │ │ │ ├── $infer_error.ts │ │ │ ├── failed.spec.ts │ │ │ ├── failed.ts │ │ │ └── readme.md │ │ ├── exact │ │ │ ├── $exact.spec.ts │ │ │ ├── $exact.ts │ │ │ └── readme.md │ │ ├── readme.md │ │ ├── special │ │ │ ├── $any.ts │ │ │ ├── $never.spec.ts │ │ │ ├── $never.ts │ │ │ ├── $special.spec.ts │ │ │ ├── $special.ts │ │ │ ├── $unknown.spec.ts │ │ │ ├── $unknown.ts │ │ │ ├── $void.ts │ │ │ └── readme.md │ │ └── utils │ │ │ ├── $merge_options.spec.ts │ │ │ ├── $merge_options.ts │ │ │ └── readme.md │ ├── any │ │ ├── any.unit.ts │ │ ├── is_any.spec.ts │ │ ├── is_any.ts │ │ ├── is_not_any.spec.ts │ │ ├── is_not_any.ts │ │ └── readme.md │ ├── array │ │ ├── array.at.spec.ts │ │ ├── array.at.ts │ │ ├── array.entries.spec.ts │ │ ├── array.entries.ts │ │ ├── array.find_last.spec.ts │ │ ├── array.find_last.ts │ │ ├── array.push.spec.ts │ │ ├── array.reverse.spec.ts │ │ ├── array.reverse.ts │ │ ├── array.slice.spec.ts │ │ ├── array.some.spec.ts │ │ ├── array.some.ts │ │ ├── array.spec.ts │ │ ├── array.unshift.spec.ts │ │ ├── array_plus.common_prop_keys.spec.ts │ │ ├── array_plus.common_prop_keys.ts │ │ ├── array_plus.concat.spec.ts │ │ ├── array_plus.concat.ts │ │ ├── array_plus.drop_match.spec.ts │ │ ├── array_plus.drop_match.ts │ │ ├── array_plus.element_match.ts │ │ ├── array_plus.element_match.unit.ts │ │ ├── array_plus.filter.spec.ts │ │ ├── array_plus.filter.ts │ │ ├── array_plus.find.spec.ts │ │ ├── array_plus.find.ts │ │ ├── array_plus.find.ts54.spec.ts │ │ ├── array_plus.find.ts55.spec.ts │ │ ├── array_plus.find.ts56.spec.ts │ │ ├── array_plus.index_at.spec.ts │ │ ├── array_plus.index_at.ts │ │ ├── array_plus.is_index_out_of_bound.spec.ts │ │ ├── array_plus.is_index_out_of_bound.ts │ │ ├── array_plus.is_readonly.spec.ts │ │ ├── array_plus.is_readonly.ts │ │ ├── array_plus.pad_start.ts │ │ ├── array_plus.spec.ts │ │ ├── array_plus.split_at.spec.ts │ │ ├── array_plus.split_at.ts │ │ ├── array_plus.ts │ │ ├── filter.spec.ts │ │ ├── filter.ts │ │ ├── find_first.spec.ts │ │ ├── find_first.ts │ │ ├── head.spec.ts │ │ ├── head.ts │ │ ├── intersect_of_props.spec.ts │ │ ├── intersect_of_props.ts │ │ ├── is_array.spec.ts │ │ ├── is_array.ts │ │ ├── is_not_array.spec.ts │ │ ├── is_not_array.ts │ │ ├── last.spec.ts │ │ ├── last.ts │ │ ├── literal_array.spec.ts │ │ ├── literal_array.ts │ │ ├── loose_array_type.spec.ts │ │ ├── loose_array_type.ts │ │ ├── pad_start.spec.ts │ │ ├── pad_start.ts │ │ ├── readme.md │ │ ├── reduce_while.spec.ts │ │ ├── reduce_while.ts │ │ ├── reverse.spec.ts │ │ ├── reverse.ts │ │ ├── union_of_props.spec.ts │ │ ├── union_of_props.ts │ │ ├── union_of_values.spec.ts │ │ └── union_of_values.ts │ ├── assertion │ │ ├── assert_type.spec.ts │ │ ├── assert_type.ts │ │ └── readme.md │ ├── bigint │ │ ├── bigint.spec.ts │ │ ├── cast.ts │ │ ├── is_bigint.spec.ts │ │ ├── is_bigint.ts │ │ ├── is_bigint_literal.spec.ts │ │ ├── is_bigint_literal.ts │ │ ├── is_not_bigint.spec.ts │ │ ├── is_not_bigint.ts │ │ ├── is_not_bigint_literal.spec.ts │ │ ├── is_not_bigint_literal.ts │ │ └── readme.md │ ├── binary │ │ ├── _binary.ts │ │ ├── bit.spec.ts │ │ ├── bit.ts │ │ └── readme.md │ ├── boolean │ │ ├── boolean.spec.ts │ │ ├── false.spec.ts │ │ ├── is_boolean.spec.ts │ │ ├── is_boolean.ts │ │ ├── is_false.spec.ts │ │ ├── is_false.ts │ │ ├── is_not_boolean.spec.ts │ │ ├── is_not_boolean.ts │ │ ├── is_not_false.spec.ts │ │ ├── is_not_false.ts │ │ ├── is_not_true.spec.ts │ │ ├── is_not_true.ts │ │ ├── is_true.spec.ts │ │ ├── is_true.ts │ │ ├── readme.md │ │ └── true.spec.ts │ ├── class │ │ ├── AnyConstructor.spec.ts │ │ ├── AnyConstructor.ts │ │ ├── index.ts │ │ ├── isConstructor.spec.ts │ │ └── isConstructor.ts │ ├── composable_types.spec.ts │ ├── composable_types.ts │ ├── equal │ │ ├── equal.spec.ts │ │ ├── equal.ts │ │ ├── identity_equal.ts │ │ ├── is_equal.spec.ts │ │ ├── is_equal.ts │ │ ├── is_not_equal.spec.ts │ │ └── readme.md │ ├── function │ │ ├── any_function.spec.ts │ │ ├── any_function.ts │ │ ├── extract_function.spec.ts │ │ ├── extract_function.ts │ │ ├── function.spec.ts │ │ ├── is_function.spec.ts │ │ ├── is_function.ts │ │ ├── is_not_function.spec.ts │ │ ├── is_not_function.ts │ │ ├── is_not_strict_function.spec.ts │ │ ├── is_not_strict_function.ts │ │ ├── is_strict_function.spec.ts │ │ ├── is_strict_function.ts │ │ └── readme.md │ ├── functional │ │ ├── ChainFn.spec.ts │ │ ├── ChainFn.ts │ │ ├── Maybe.spec.ts │ │ ├── Maybe.ts │ │ ├── compose.spec.ts │ │ ├── compose.ts │ │ ├── context.spec.ts │ │ ├── context.ts │ │ └── index.ts │ ├── index.ts │ ├── json.spec.ts │ ├── json.ts │ ├── logical │ │ ├── logical.spec.ts │ │ ├── logical.ts │ │ └── readme.md │ ├── math │ │ ├── README.md │ │ ├── abs.spec.ts │ │ ├── abs.ts │ │ ├── add.spec.ts │ │ ├── add.ts │ │ ├── greater_than.spec.ts │ │ ├── greater_than.ts │ │ ├── index.ts │ │ ├── math_plus.to_negative.spec.ts │ │ ├── math_plus.to_negative.ts │ │ ├── math_plus.ts │ │ ├── max.spec.ts │ │ ├── max.ts │ │ ├── multiply.spec.ts │ │ ├── multiply.ts │ │ ├── numeric_struct.digit.unit.ts │ │ ├── numeric_struct.digit_array.unit.ts │ │ ├── numeric_struct.digits_struct.unit.ts │ │ ├── numeric_struct.ts │ │ ├── numeric_struct.unit.ts │ │ ├── subtract.spec.ts │ │ └── subtract.ts │ ├── mix_types │ │ ├── box.spec.ts │ │ ├── box.ts │ │ ├── exclude.spec.ts │ │ ├── exclude.ts │ │ ├── is_any_or_never.spec.ts │ │ ├── is_any_or_never.ts │ │ ├── merge.spec.ts │ │ ├── merge.ts │ │ └── readme.md │ ├── never │ │ ├── is_never.spec.ts │ │ ├── is_never.ts │ │ ├── is_not_never.spec.ts │ │ ├── is_not_never.ts │ │ ├── never.unit.ts │ │ └── readme.md │ ├── nodejs │ │ ├── index.ts │ │ ├── isNodeError.spec.ts │ │ └── isNodeError.ts │ ├── nominal │ │ ├── brand.spec.ts │ │ ├── brand.ts │ │ ├── constants.ts │ │ ├── flavor.spec.ts │ │ ├── flavor.ts │ │ ├── index.ts │ │ ├── nominal_match.spec.ts │ │ └── nominal_match.ts │ ├── null │ │ ├── is_not_null.spec.ts │ │ ├── is_not_null.ts │ │ ├── is_null.spec.ts │ │ ├── is_null.ts │ │ ├── null.spec.ts │ │ └── readme.md │ ├── number │ │ ├── cast.ts │ │ ├── is_not_number.spec.ts │ │ ├── is_not_number.ts │ │ ├── is_not_number_literal.spec.ts │ │ ├── is_not_number_literal.ts │ │ ├── is_number.spec.ts │ │ ├── is_number.ts │ │ ├── is_number_literal.spec.ts │ │ ├── is_number_literal.ts │ │ ├── number.spec.ts │ │ ├── number_array.sum.spec.ts │ │ ├── number_array.ts │ │ ├── number_plus.spec.ts │ │ ├── number_plus.ts │ │ └── readme.md │ ├── numeric │ │ ├── cast.numeric_to_string.spec.ts │ │ ├── cast.string_to_numeric.spec.ts │ │ ├── cast.ts │ │ ├── is_integer.spec.ts │ │ ├── is_integer.ts │ │ ├── is_negative.spec.ts │ │ ├── is_negative.ts │ │ ├── is_not_integer.spec.ts │ │ ├── is_not_integer.ts │ │ ├── is_not_negative.spec.ts │ │ ├── is_not_negative.ts │ │ ├── is_not_numeric.spec.ts │ │ ├── is_not_numeric.ts │ │ ├── is_not_positive.spec.ts │ │ ├── is_not_positive.ts │ │ ├── is_numeric.spec.ts │ │ ├── is_numeric.ts │ │ ├── is_positive.spec.ts │ │ ├── is_positive.ts │ │ ├── numeric.zero.spec.ts │ │ ├── numeric_plus.spec.ts │ │ ├── numeric_plus.ts │ │ ├── numeric_type.ts │ │ └── readme.md │ ├── object │ │ ├── ANotB.spec.ts │ │ ├── ANotB.ts │ │ ├── ExcludePropType.spec.ts │ │ ├── ExcludePropType.ts │ │ ├── IsDisjoint.spec.ts │ │ ├── IsDisjoint.ts │ │ ├── IsRecord.spec.ts │ │ ├── IsRecord.ts │ │ ├── KeyTypes.spec.ts │ │ ├── KeyTypes.ts │ │ ├── KeyofOptional.spec.ts │ │ ├── KeyofOptional.ts │ │ ├── KeysWithDiffType.spec.ts │ │ ├── KeysWithDiffType.ts │ │ ├── KnownKeys.spec.ts │ │ ├── KnownKeys.ts │ │ ├── OptionalKeys.spec.ts │ │ ├── Partial.spec.ts │ │ ├── Partial.ts │ │ ├── RecursiveIntersect.spec.ts │ │ ├── RecursiveIntersect.ts │ │ ├── RecursiveRequired.spec.ts │ │ ├── RecursiveRequired.ts │ │ ├── Required.spec.ts │ │ ├── Required.ts │ │ ├── RequiredKeys.spec.ts │ │ ├── RequiredKeys.ts │ │ ├── SpreadRecord.spec.ts │ │ ├── SpreadRecord.ts │ │ ├── ValueOf.spec.ts │ │ ├── ValueOf.ts │ │ ├── adjust_exact_optional_props.spec.ts │ │ ├── adjust_exact_optional_props.ts │ │ ├── any_record.spec.ts │ │ ├── any_record.ts │ │ ├── everyKey.spec.ts │ │ ├── everyKey.ts │ │ ├── facade.spec.ts │ │ ├── facade.ts │ │ ├── filterKey.spec.ts │ │ ├── filterKey.ts │ │ ├── findKey.spec.ts │ │ ├── findKey.ts │ │ ├── forEachKey.spec.ts │ │ ├── forEachKey.ts │ │ ├── getField.spec.ts │ │ ├── getField.ts │ │ ├── hasKey.spec.ts │ │ ├── hasKey.ts │ │ ├── hasProperty.spec.ts │ │ ├── hasProperty.ts │ │ ├── index.ts │ │ ├── is_not_object.spec.ts │ │ ├── is_not_object.ts │ │ ├── is_object.spec.ts │ │ ├── is_object.ts │ │ ├── left_join.spec.ts │ │ ├── left_join.ts │ │ ├── mapKey.spec.ts │ │ ├── mapKey.ts │ │ ├── mapProperties.spec.ts │ │ ├── mapProperties.ts │ │ ├── merge.spec.ts │ │ ├── merge.ts │ │ ├── object.spec.ts │ │ ├── object_plus.ts │ │ ├── omit.spec.ts │ │ ├── omit.ts │ │ ├── omit.unit.ts │ │ ├── optional_key.spec.ts │ │ ├── optional_key.ts │ │ ├── pick.spec.ts │ │ ├── pick.ts │ │ ├── properties.spec.ts │ │ ├── properties.ts │ │ ├── readme.md │ │ ├── record.spec.ts │ │ ├── record.ts │ │ ├── recursive_partial.spec.ts │ │ ├── recursive_partial.ts │ │ ├── reduceKey.spec.ts │ │ ├── reduceKey.ts │ │ ├── replaceProperty.spec.ts │ │ ├── replaceProperty.ts │ │ ├── someKey.spec.ts │ │ ├── someKey.ts │ │ ├── split.spec.ts │ │ ├── split.ts │ │ ├── typeOverrideIncompatible.spec.ts │ │ └── typeOverrideIncompatible.ts │ ├── predicates │ │ ├── CanAssign.spec.ts │ │ ├── CanAssign.ts │ │ ├── Extends.ts │ │ ├── If.spec.ts │ │ ├── If.ts │ │ ├── IsEmptyObject.spec.ts │ │ ├── IsEmptyObject.ts │ │ ├── assignability.strict.spec.ts │ │ ├── assignable.spec.ts │ │ ├── assignable.ts │ │ ├── index.spec.ts │ │ ├── index.ts │ │ ├── literal.spec.ts │ │ ├── literal.ts │ │ ├── not_assignable.spec.ts │ │ └── not_assignable.ts │ ├── primitive.spec.ts │ ├── primitive.ts │ ├── promise │ │ ├── MaybePromise.spec.ts │ │ ├── MaybePromise.ts │ │ ├── PromiseValue.spec.ts │ │ ├── PromiseValue.ts │ │ ├── PromiseValueMerge.spec.ts │ │ ├── PromiseValueMerge.ts │ │ ├── index.ts │ │ ├── isPromise.spec.ts │ │ ├── isPromise.ts │ │ ├── mapSeries.spec.ts │ │ └── mapSeries.ts │ ├── string │ │ ├── $extract_manipulated_string.spec.ts │ │ ├── $extract_manipulated_string.ts │ │ ├── _string_type.ts │ │ ├── _string_type.unit.ts │ │ ├── is_not_string.spec.ts │ │ ├── is_not_string.ts │ │ ├── is_not_string_literal.spec.ts │ │ ├── is_not_string_literal.ts │ │ ├── is_not_template_literal.spec.ts │ │ ├── is_not_template_literal.ts │ │ ├── is_string.spec.ts │ │ ├── is_string.ts │ │ ├── is_string_literal.spec.ts │ │ ├── is_string_literal.ts │ │ ├── is_template_literal.spec.ts │ │ ├── is_template_literal.ts │ │ ├── readme.md │ │ ├── string.includes.spec.ts │ │ ├── string.split.spec.ts │ │ ├── string.ts │ │ ├── string_plus.spec.ts │ │ └── string_plus.ts │ ├── symbol │ │ ├── is_not_symbol.spec.ts │ │ ├── is_not_symbol.ts │ │ ├── is_symbol.spec.ts │ │ ├── is_symbol.ts │ │ ├── readme.md │ │ └── symbol.spec.ts │ ├── testing │ │ ├── readme.md │ │ ├── stub.build.spec.ts │ │ ├── stub.builder.spec.ts │ │ ├── stub.spec.ts │ │ ├── stub.ts │ │ ├── test_type.array.spec.ts │ │ ├── test_type.bigint.spec.ts │ │ ├── test_type.boolean.spec.ts │ │ ├── test_type.can_assign.spec.ts │ │ ├── test_type.equal.spec.ts │ │ ├── test_type.false.spec.ts │ │ ├── test_type.function.spec.ts │ │ ├── test_type.inspect.spec.ts │ │ ├── test_type.never.spec.ts │ │ ├── test_type.null.spec.ts │ │ ├── test_type.number.spec.ts │ │ ├── test_type.object.spec.ts │ │ ├── test_type.strict_bigint.spec.ts │ │ ├── test_type.strict_boolean.spec.ts │ │ ├── test_type.strict_can_assign.spec.ts │ │ ├── test_type.strict_function.spec.ts │ │ ├── test_type.strict_number.spec.ts │ │ ├── test_type.strict_string.spec.ts │ │ ├── test_type.string.spec.ts │ │ ├── test_type.symbol.spec.ts │ │ ├── test_type.true.spec.ts │ │ ├── test_type.ts │ │ ├── test_type.tuple.spec.ts │ │ ├── test_type.undefined.spec.ts │ │ ├── test_type.unknown.spec.ts │ │ └── test_type.void.spec.ts │ ├── tuple │ │ ├── common_prop_keys.spec.ts │ │ ├── common_prop_keys.ts │ │ ├── create_tuple.spec.ts │ │ ├── create_tuple.ts │ │ ├── drop.spec.ts │ │ ├── drop.ts │ │ ├── drop_first.spec.ts │ │ ├── drop_last.spec.ts │ │ ├── drop_match.spec.ts │ │ ├── is_not_tuple.spec.ts │ │ ├── is_not_tuple.ts │ │ ├── is_tuple.spec.ts │ │ ├── is_tuple.ts │ │ ├── readme.md │ │ ├── tail.spec.ts │ │ ├── tail.ts │ │ ├── tuple.spec.ts │ │ ├── tuple_plus.common_prop_keys.spec.ts │ │ ├── tuple_plus.common_prop_keys.ts │ │ ├── tuple_plus.drop_match.spec.ts │ │ ├── tuple_plus.drop_match.ts │ │ ├── tuple_plus.filter.ts │ │ ├── tuple_plus.find.spec.ts │ │ ├── tuple_plus.find.ts │ │ ├── tuple_plus.find.ts54.spec.ts │ │ ├── tuple_plus.find.ts55.spec.ts │ │ ├── tuple_plus.pad_start.spec.ts │ │ ├── tuple_plus.pad_start.ts │ │ ├── tuple_plus.ts │ │ ├── tuple_type.distributive.spec.ts │ │ └── type_plus.filter.spec.ts │ ├── type-guard │ │ ├── is_type.never.spec.ts │ │ ├── is_type.spec.ts │ │ ├── is_type.ts │ │ └── readme.md │ ├── undefined │ │ ├── has_undefined.spec.ts │ │ ├── has_undefined.ts │ │ ├── is_not_undefined.spec.ts │ │ ├── is_not_undefined.ts │ │ ├── is_undefined.spec.ts │ │ ├── is_undefined.ts │ │ ├── readme.md │ │ └── undefined.spec.ts │ ├── union │ │ ├── readme.md │ │ ├── sub_union.spec.ts │ │ ├── sub_union.ts │ │ ├── union.is_union.spec.ts │ │ ├── union.ts │ │ ├── union.union_type.spec.ts │ │ └── union_to_intersection.ts │ ├── union_keys.spec.ts │ ├── union_keys.ts │ ├── unknown │ │ ├── is_not_unknown.spec.ts │ │ ├── is_not_unknown.ts │ │ ├── is_unknown.spec.ts │ │ ├── is_unknown.ts │ │ ├── not_unknown_or.spec.ts │ │ ├── not_unknown_or.ts │ │ ├── readme.md │ │ └── unknown.unit.ts │ ├── unpartial.ts │ ├── utils │ │ ├── Widen.ts │ │ ├── as.spec.ts │ │ ├── as.ts │ │ ├── excessive.ts │ │ ├── index.spec.ts │ │ ├── index.ts │ │ ├── inspect.spec.ts │ │ ├── inspect.ts │ │ ├── no_infer.built-in.unit.ts │ │ ├── no_infer.spec.ts │ │ ├── no_infer.ts │ │ ├── options.merge.unit.ts │ │ ├── options.ts │ │ └── readme.md │ └── void │ │ ├── is_not_void.spec.ts │ │ ├── is_not_void.ts │ │ ├── is_void.spec.ts │ │ ├── is_void.ts │ │ ├── readme.md │ │ └── void.spec.ts │ ├── tsconfig.base.json │ ├── tsconfig.cjs.json │ ├── tsconfig.esm.json │ ├── tsconfig.json │ ├── tsconfig.ts54.json │ ├── tsconfig.ts55.json │ └── tsconfig.ts56.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── readme.md ├── slides ├── _revealjs │ ├── style-basic.css │ └── uni.png ├── test_type.md └── tuple_plus.pad_start.md ├── turbo.json └── type-plus.code-workspace /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/blue-pumpkins-accept.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Update `IsSymbol` and variances 6 | -------------------------------------------------------------------------------- /.changeset/bright-ghosts-count.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Update `IsUnknown` and `IsNotUnknown`. Remove `UnknownType` and `NotUnknownType`. 6 | -------------------------------------------------------------------------------- /.changeset/calm-cats-beam.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Fix `IsPositive` should return `boolean` 6 | -------------------------------------------------------------------------------- /.changeset/chilly-rules-applaud.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Fix `$ResolveOptions` type to handle `undefined` value. 6 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json", 3 | "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { 4 | "onlyUpdatePeerDependentsWhenOutOfRange": true, 5 | "updateInternalDependents": "always" 6 | }, 7 | "access": "public", 8 | "baseBranch": "main", 9 | "changelog": "@changesets/cli/changelog", 10 | "commit": false, 11 | "fixed": [], 12 | "ignore": ["website"], 13 | "linked": [], 14 | "updateInternalDependencies": "patch" 15 | } 16 | -------------------------------------------------------------------------------- /.changeset/dirty-stingrays-greet.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Move source under `packages/type-plus`. 6 | -------------------------------------------------------------------------------- /.changeset/dull-owls-trade.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Rename `$ExactOptions` to `$Exact.Options`, 6 | `$ExactDefault` to `$Exact.Default`, 7 | `$IsExact` to `$Exact.Parse` 8 | -------------------------------------------------------------------------------- /.changeset/fast-dolls-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Replace `Equal` with the new `$Equal` type. 6 | -------------------------------------------------------------------------------- /.changeset/fast-peaches-occur.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Rename `$IsDistributive` to `$Distributive.Parse`. 6 | -------------------------------------------------------------------------------- /.changeset/fluffy-files-flash.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Optimize `SplitAt` by moving never check of `DeleteCount` to the top. 6 | -------------------------------------------------------------------------------- /.changeset/fresh-spiders-invite.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Removing incorrect usage of the `typesVersions` field in `package.json`. 6 | 7 | -------------------------------------------------------------------------------- /.changeset/gentle-papayas-beg.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | fix `IsArray` 6 | -------------------------------------------------------------------------------- /.changeset/good-cups-approve.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Remove `exports.default` in `package.json`. 6 | 7 | That provide the wrong file to systems expecting CJS. 8 | May need to add a different one for browser-spec. 9 | -------------------------------------------------------------------------------- /.changeset/good-squids-yawn.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | `$Equality` to `$Equal`. 6 | -------------------------------------------------------------------------------- /.changeset/grumpy-bananas-jog.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Deprecate `NoInfer`. 6 | -------------------------------------------------------------------------------- /.changeset/itchy-cups-complain.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Remove `$Override`. It is not needed. 6 | -------------------------------------------------------------------------------- /.changeset/itchy-mails-sleep.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Simplify `Omit` type as the simpler code is working with typescript 5.4 6 | -------------------------------------------------------------------------------- /.changeset/itchy-planes-remember.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Update bit types 6 | -------------------------------------------------------------------------------- /.changeset/khaki-queens-lay.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Consolidate `$Selection` types. 6 | -------------------------------------------------------------------------------- /.changeset/khaki-yaks-unite.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Deprecate `Failed` and `FailedT`. 6 | -------------------------------------------------------------------------------- /.changeset/large-windows-obey.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Update `IsFunction`, `IsNotFunction`, `IsStrictFunction`, `IsNotStrictFunction`. 6 | 7 | Remove `FunctionType`, `NotFunctionType`, `StrictFunctionType`, `NotStrictFunctionType`. 8 | -------------------------------------------------------------------------------- /.changeset/late-seals-lick.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Work around excessive stack depth error in TS 5.4 for type `Zeros`. 6 | -------------------------------------------------------------------------------- /.changeset/little-donkeys-pay.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Update to require typescript 5.4. 6 | -------------------------------------------------------------------------------- /.changeset/lovely-horses-press.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `IsStrictObject` type. 6 | -------------------------------------------------------------------------------- /.changeset/lucky-socks-walk.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Rename `case*` to `$*` to make them easier to use. 6 | -------------------------------------------------------------------------------- /.changeset/metal-buckets-stare.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Add docs for `ArrayPlus.IsReadonly`. 6 | 7 | The following are internal changes thus not considered a breaking change: 8 | 9 | - Replace `MergeOptions`/`MergeCases` with `TypePlusOptions.Merge`. 10 | - Rename `TypePlusOptions.Predicate` to `TypePlusOptions.Selection`. 11 | -------------------------------------------------------------------------------- /.changeset/mighty-pumas-wink.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Update `$Type` to work with primitive types and improve object type usage. 6 | -------------------------------------------------------------------------------- /.changeset/moody-badgers-refuse.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Remove `StrictBigintType` and `NotStrictBigintType` 6 | -------------------------------------------------------------------------------- /.changeset/nice-forks-hide.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Accept `readonly` for array. 6 | Add `ArrayPlus.IsReadonly`. 7 | 8 | Fix `ArrayPlus.Reverse` to support `readonly`. 9 | -------------------------------------------------------------------------------- /.changeset/ninety-dolphins-refuse.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Rename `$SpecialType` to `$Special`. 6 | -------------------------------------------------------------------------------- /.changeset/odd-panthers-jog.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `$Void` support. 6 | -------------------------------------------------------------------------------- /.changeset/polite-games-guess.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Use `fn.apply` instead of spread. 6 | It has better performance and function that are already bound continue to work as expected. 7 | 8 | ```ts 9 | const bindFn = fn.bind(This) 10 | 11 | bindFn.apply(null, args) // `this` is not affected 12 | ``` 13 | -------------------------------------------------------------------------------- /.changeset/purple-countries-decide.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Update some docs under `numerics` 6 | -------------------------------------------------------------------------------- /.changeset/quick-grapes-travel.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Reintroduce CJS support. 6 | -------------------------------------------------------------------------------- /.changeset/red-plums-look.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `Merge` and `ObjectPlus.Merge` 6 | -------------------------------------------------------------------------------- /.changeset/rich-spies-sneeze.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Update `Equal` signature and implementation. 6 | Deprecate `IsEqual` and `IsNotEqual` in favor of `Equal`. 7 | -------------------------------------------------------------------------------- /.changeset/selfish-sheep-explain.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Rename `$DistributiveOptions` to `$Distributive.Options` and `$DistributiveDefault` to `$Distributive.Default`. 6 | -------------------------------------------------------------------------------- /.changeset/serious-deers-cheer.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Remove `$Exact` namespace. 6 | `$Exact.$Options` is now `$ExactOptions`. 7 | `$Exact.$Default` is now `$ExactDefault`. 8 | `$Exact.$IsExact` is now `$IsExact`. 9 | 10 | -------------------------------------------------------------------------------- /.changeset/serious-suns-drive.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `Assignable`. 6 | Deprecated `CanAssign` and `StrictCanAssign`. 7 | -------------------------------------------------------------------------------- /.changeset/seven-peaches-explode.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Rename `$Select` to `$Equal`. 6 | Remove `$SelectStrict`, use `$Equal` instead. 7 | -------------------------------------------------------------------------------- /.changeset/shaggy-roses-hug.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Remove `NonUndefined`. Use `Exclude` instead. 6 | Remove `NonNull`. Use `Exclude` instead. 7 | -------------------------------------------------------------------------------- /.changeset/sharp-walls-deliver.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Fix `IsInteger` should returns `boolean` 6 | -------------------------------------------------------------------------------- /.changeset/short-suns-stare.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add customize support for `ArrayPlus.Filter` 6 | -------------------------------------------------------------------------------- /.changeset/silly-hornets-train.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Updato logical types. 6 | -------------------------------------------------------------------------------- /.changeset/silver-eggs-add.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Update and release as ESM package only. 6 | -------------------------------------------------------------------------------- /.changeset/sixty-ears-fry.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `$Void` support to `IsAny` and `IsNotAny`. 6 | -------------------------------------------------------------------------------- /.changeset/strong-months-behave.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Fix `IsNegative` should return `boolean` 6 | -------------------------------------------------------------------------------- /.changeset/strong-rabbits-tie.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Remove `Positive`, `Negative`, `NumericType`, `NotNumericType`, 6 | -------------------------------------------------------------------------------- /.changeset/sweet-tools-clap.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `$DefineInputOptions` and `$DefineBranchOptions`. 6 | Add support of handing `$any`, `$unknown`, `$never` for `IsAny`. 7 | 8 | Add `$ResolveBranch` that fixes the `unknown` pass-through issue. 9 | 10 | The types will be able to use in the form of `IsAny`, hopefully. 11 | 12 | It's still not recommended doing so, but at least it will not produce weird results. 13 | 14 | Will need to convert other types to support that and add tests for them. 15 | -------------------------------------------------------------------------------- /.changeset/tasty-numbers-chew.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Add `SubUnion` type. 6 | -------------------------------------------------------------------------------- /.changeset/three-pants-hunt.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Change `$ResolveBranch` type parameters order. 6 | `T` is moved to the last position as `D` (as it is the default value), and made optional. 7 | -------------------------------------------------------------------------------- /.changeset/tidy-coins-relate.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Update `IsTuple` and variances. 6 | -------------------------------------------------------------------------------- /.changeset/tough-files-sort.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": major 3 | --- 4 | 5 | Update `IsString` and its variances. 6 | -------------------------------------------------------------------------------- /.changeset/tough-forks-dance.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `AdjustExactOptionalProps`. 6 | -------------------------------------------------------------------------------- /.changeset/tricky-wasps-glow.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Add `Box` to box primitive types to their boxed types. 6 | -------------------------------------------------------------------------------- /.changeset/twelve-shirts-lay.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": patch 3 | --- 4 | 5 | Remove `$Exact` branch type. It is not needed anymore. 6 | -------------------------------------------------------------------------------- /.changeset/two-shrimps-wonder.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Update `IsObject` and variants 6 | -------------------------------------------------------------------------------- /.changeset/wise-zoos-jog.md: -------------------------------------------------------------------------------- 1 | --- 2 | "type-plus": minor 3 | --- 4 | 5 | Export `$Exact`. 6 | -------------------------------------------------------------------------------- /.config/typedoc.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://typedoc.org/schema.json", 3 | "titleLink": "https://github.com/unional/type-plus", 4 | "entryPointStrategy": "packages", 5 | "includeVersion": true, 6 | "entryPoints": ["../packages/type-plus"], 7 | "out": "../docs" 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_size = 2 11 | indent_style = tab 12 | trim_trailing_whitespace = true 13 | 14 | [*.{md,mdx}] 15 | max_line_length = off 16 | 17 | [*.{yml,yaml}] 18 | indent_style = space 19 | insert_final_newline = false 20 | max_line_length = off 21 | 22 | [*.{json,code-workspace}] 23 | insert_final_newline = false 24 | 25 | [.vscode/ltex.*.txt] 26 | insert_final_newline = false 27 | 28 | [.changeset/*.json] 29 | indent_style = space 30 | insert_final_newline = true 31 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/codeql/codeql-config.yml: -------------------------------------------------------------------------------- 1 | paths: 2 | - packages/*/src 3 | paths-ignore: 4 | - '**/*.spec.ts' 5 | -------------------------------------------------------------------------------- /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: automatic merge for Dependabot pull requests 3 | conditions: 4 | - author~=^dependabot(|-preview)\[bot\]$ 5 | - title~=bump [^\s]+ from ([\d]+)\..+ to \1\. 6 | actions: 7 | merge: 8 | method: squash 9 | - name: automatic merge for Renovate pull requests 10 | conditions: 11 | - author=renovate[bot] 12 | - and: 13 | - head~=^(?!major-) 14 | - -title~=update dependency @types\/node 15 | - -title~=update dependency ts- 16 | actions: 17 | merge: 18 | method: squash 19 | - name: automatic merge for Snyk pull requests 20 | conditions: 21 | - title~=^\[Snyk\] 22 | - head~=^snyk-fix 23 | - check-success~=^security/snyk 24 | actions: 25 | merge: 26 | method: squash 27 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["github>unional/renovate-preset"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: pull-request 2 | on: 3 | pull_request: 4 | types: [opened, synchronize] 5 | branches-ignore: 6 | - changeset-release/main 7 | jobs: 8 | code: 9 | uses: unional/.github/.github/workflows/pnpm-verify.yml@main 10 | with: 11 | os: '["ubuntu-latest"]' 12 | secrets: inherit 13 | # codecov: 14 | # runs-on: ubuntu-latest 15 | # steps: 16 | # - name: codecov 17 | # needs: code 18 | # uses: codecov/codecov-action@v4.5.0 19 | # env: 20 | # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: [main, v7] 5 | 6 | jobs: 7 | code: 8 | uses: unional/.github/.github/workflows/pnpm-verify.yml@main 9 | with: 10 | os: '["ubuntu-latest"]' 11 | 12 | release: 13 | uses: unional/.github/.github/workflows/pnpm-release-changeset.yml@main 14 | needs: code 15 | secrets: inherit 16 | 17 | docgen: 18 | uses: unional/.github/.github/workflows/pnpm-docs.yml@main 19 | needs: release 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | .nyc_output 20 | .progress 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directory 32 | jspm_packages 33 | node_modules 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | 41 | # webstorm 42 | .idea 43 | 44 | # yarn 45 | .pnp.* 46 | .yarn/* 47 | !.yarn/patches 48 | !.yarn/plugins 49 | !.yarn/releases 50 | !.yarn/sdks 51 | !.yarn/versions 52 | 53 | # project 54 | /.turbo 55 | *.tsbuildinfo 56 | /docs 57 | */tslib/ 58 | # old location 59 | /type-plus 60 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pnpm commitlint --edit $1 3 | -------------------------------------------------------------------------------- /.markdownlint-cli2.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "ignores": [".changeset/*.md", "slides/*.md", "CHANGELOG.md", "flow-types.md", "LICENSE"], 3 | "config": { 4 | "hard_tab": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["main", "next", "beta"], 3 | "plugins": [ 4 | "@semantic-release/commit-analyzer", 5 | "@semantic-release/release-notes-generator", 6 | [ 7 | "@semantic-release/changelog", 8 | { 9 | "changelogFile": "CHANGELOG.md" 10 | } 11 | ], 12 | "@semantic-release/npm", 13 | "@semantic-release/github", 14 | [ 15 | "@semantic-release/git", 16 | { 17 | "assets": ["CHANGELOG.md"] 18 | } 19 | ] 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "aaron-bond.better-comments", 4 | "astro-build.astro-vscode", 5 | "bierner.emojisense", 6 | "davidanson.vscode-markdownlint", 7 | "dbaeumer.vscode-eslint", 8 | "drknoxy.eslint-disable-snippets", 9 | "eamodio.gitlens", 10 | "editorconfig.editorconfig", 11 | "evilz.vscode-reveal", 12 | "github.vscode-pull-request-github", 13 | "mhutchie.git-graph", 14 | "pflannery.vscode-versionlens", 15 | "unional.vscode-sort-package-json", 16 | "yzhang.markdown-all-in-one", 17 | "ms-vscode.live-server" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./apps/website/node_modules/.bin/astro dev", 6 | "name": "Astro Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/ltex.dictionary.en-US.txt: -------------------------------------------------------------------------------- 1 | Codecov 2 | ASI 3 | PropType 4 | @gcanti 5 | Homa 6 | unional 7 | NONINFRINGEMENT 8 | util 9 | bigint 10 | endofunctor 11 | endofunction 12 | Endofunction 13 | Astro 14 | Vercel 15 | StackBlitz 16 | CodeSandbox 17 | -------------------------------------------------------------------------------- /.vscode/type-plus.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "name": "type-plus", 5 | "path": ".." 6 | }, 7 | { 8 | "name": "typescript-blackbook", 9 | "path": "../../typescript-blackbook" 10 | }, 11 | { 12 | "path": "../../.github" 13 | } 14 | ], 15 | "settings": { 16 | "markdown.extension.list.toggle.candidate-markers": ["-", "1."], 17 | "markdown.extension.toc.levels": "2..6", 18 | "markdown.extension.toc.omittedFromToc": { 19 | "type-plus/README.md": ["## Table of Contents", "## Installation"] 20 | }, 21 | "markdown.extension.toc.orderedList": true, 22 | "markdown.extension.toc.unorderedList.marker": "-", 23 | "material-icon-theme.files.associations": { 24 | "tsconfig.ts54.json": "tsconfig", 25 | "tsconfig.ts55.json": "tsconfig", 26 | "tsconfig.ts56.json": "tsconfig" 27 | }, 28 | "typescript.tsdk": "node_modules/typescript/lib", 29 | "cSpell.words": ["astrolib"] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/website/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /apps/website/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import starlight from '@astrojs/starlight' 2 | // @ts-check 3 | import { defineConfig } from 'astro/config' 4 | 5 | // https://astro.build/config 6 | export default defineConfig({ 7 | integrations: [ 8 | starlight({ 9 | title: 'type-plus', 10 | logo: { 11 | light: '/public/type-plus.svg', 12 | dark: '/public/type-plus.svg', 13 | }, 14 | description: 15 | 'Provides over 200 utility types and functions for applications, library, and type-level programming.', 16 | social: { 17 | github: 'https://github.com/unional/type-plus', 18 | }, 19 | sidebar: [ 20 | { 21 | label: 'Guides', 22 | items: [ 23 | // Each item here is one entry in the navigation menu. 24 | { label: 'Getting Started', link: '/guides/getting-started/' }, 25 | ], 26 | }, 27 | { 28 | label: 'API', 29 | autogenerate: { directory: 'api' }, 30 | }, 31 | { 32 | label: 'Reference', 33 | autogenerate: { directory: 'reference' }, 34 | }, 35 | ], 36 | }), 37 | ], 38 | }) 39 | -------------------------------------------------------------------------------- /apps/website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "private": true, 4 | "type": "module", 5 | "version": "0.0.1", 6 | "scripts": { 7 | "dev": "astro dev", 8 | "start": "astro dev", 9 | "build": "astro check && astro build", 10 | "preview": "astro preview", 11 | "astro": "astro" 12 | }, 13 | "dependencies": { 14 | "@astrojs/check": "^0.9.4", 15 | "@astrojs/starlight": "^0.32.0", 16 | "astro": "^5.1.2", 17 | "sharp": "^0.34.0" 18 | } 19 | } -------------------------------------------------------------------------------- /apps/website/public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/website/public/type-plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | tp 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /apps/website/src/assets/houston.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unional/type-plus/07ccb2ae804589ce3a6f1a9ad5e52ed69c279fd3/apps/website/src/assets/houston.webp -------------------------------------------------------------------------------- /apps/website/src/content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection } from 'astro:content' 2 | import { docsLoader } from '@astrojs/starlight/loaders' 3 | import { docsSchema } from '@astrojs/starlight/schema' 4 | 5 | export const collections = { 6 | docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), 7 | } 8 | -------------------------------------------------------------------------------- /apps/website/src/content/docs/guides/example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Example Guide 3 | description: A guide in my new Starlight docs site. 4 | --- 5 | 6 | Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. 7 | Writing a good guide requires thinking about what your users are trying to do. 8 | 9 | ## Further reading 10 | 11 | - Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework 12 | -------------------------------------------------------------------------------- /apps/website/src/content/docs/guides/getting-started.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | --- 4 | 5 | [`type-plus`] provides over 200 utility types and functions for applications, library, and type-level programming. 6 | 7 | ## Installation 8 | 9 | ```sh 10 | npm install type-plus 11 | 12 | yarn add type-plus 13 | 14 | pnpm add type-plus 15 | ``` 16 | 17 | [`type-plus`]: https://github.com/unional/type-plus 18 | -------------------------------------------------------------------------------- /apps/website/src/content/docs/guides/typescript-version-compatibility.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: TypeScript Version Compatibility 3 | --- 4 | 5 | [TypeScript] version does not follow the [Semantic Versioning] specification. 6 | That means every release of TypeScript may have breaking changes. 7 | 8 | Being a type library, especially a type library with a lot of advanced types, 9 | [`type-plus`] is at the forefront of this battleground. 10 | 11 | Starting from version 8.0.0, 12 | [`type-plus`] will be more compatible with various versions of TypeScript. 13 | Depends on which typescript you are using, [`type-plus`] will be loaded with a set of types that are compatible with that version of TypeScript. 14 | 15 | [`type-plus`]: https://github.com/unional/type-plus 16 | [TypeScript]: https://www.typescriptlang.org/ 17 | [Semantic Versioning]: https://semver.org/ 18 | -------------------------------------------------------------------------------- /apps/website/src/content/docs/reference/status.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Status 3 | --- 4 | 5 | ## 🏷️ Since 6 | 7 | > 🏷️ :label: 8 | 9 | Introduced in the specified release: 10 | 11 | ```md 12 | 🏷️ **since 8.0.0** 13 | ``` 14 | 15 | ## 🐞 Known Issue 16 | 17 | > 🐞 :lady_beetle: 18 | 19 | These types have known issues or limitations caused by the approach they take 20 | or limitations of TypeScript. 21 | 22 | ## 💀 Deprecated 23 | 24 | > 💀 :skull: 25 | 26 | Deprecated type (💀 deprecated) is a type that is deprecated and will be removed soon. 27 | 28 | ## 🗑️ Removed 29 | 30 | > 🗑️ :wastebasket: 31 | 32 | Removed type is a type that is removed and will not be available anymore. 33 | It will remain in the documentation for reference purpose. 34 | -------------------------------------------------------------------------------- /apps/website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": [".astro/types.d.ts", "**/*"], 4 | "exclude": ["dist"] 5 | } 6 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "extends": ["@repobuddy/biome/recommended"], 4 | "vcs": { 5 | "defaultBranch": "main", 6 | "clientKind": "git", 7 | "enabled": true, 8 | "useIgnoreFile": true 9 | }, 10 | "files": { 11 | "ignore": [ 12 | ".changeset/*.json", 13 | ".turbo", 14 | ".vscode/*.txt", 15 | ".vscode/*.code-snippets", 16 | "./docs", 17 | "packages/*/coverage", 18 | "packages/*/cjs", 19 | "packages/*/dist", 20 | "packages/*/esm", 21 | "packages/*/tslib", 22 | "old/checker", 23 | "packages/type-plus/src/types", 24 | "packages/type-plus/src/type-checker", 25 | "apps/website/dist", 26 | "_revealjs", 27 | "*.astro", 28 | "package.json" 29 | ] 30 | }, 31 | "linter": { 32 | "rules": { 33 | "complexity": { 34 | "noBannedTypes": "off" 35 | }, 36 | "suspicious": { 37 | "noConfusingVoidType": "off" 38 | } 39 | } 40 | }, 41 | "organizeImports": { 42 | "enabled": true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /commitlint.config.cjs: -------------------------------------------------------------------------------- 1 | const DISABLE = [0] 2 | 3 | module.exports = { 4 | extends: ['@commitlint/config-conventional'], 5 | rules: { 6 | 'body-max-line-length': DISABLE, 7 | 'footer-max-line-length': DISABLE, 8 | 'subject-case': DISABLE, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import yml from 'eslint-plugin-yml' 2 | 3 | export default [ 4 | { 5 | ignores: ['**/coverage/', '**/dist/', '**/cjs/', '**/esm/'], 6 | }, 7 | ...yml.configs['flat/standard'], 8 | { 9 | ignores: ['.*/'], 10 | rules: { 11 | 'yml/quotes': ['error', { prefer: 'single' }], 12 | }, 13 | }, 14 | ] 15 | -------------------------------------------------------------------------------- /flow-types.md: -------------------------------------------------------------------------------- 1 | - $Keys 2 | - $Values 3 | - $ReadOnly 4 | - $Exact 5 | - $Diff 6 | - $Rest 7 | - $PropertyType 8 | - $ElementType 9 | - $NonMaybeType 10 | - $ObjMap 11 | - $TupleMap 12 | - $Call 13 | - Class 14 | - $Shape 15 | - $Supertype 16 | - $Subtype 17 | - Existential Type (*) 18 | -------------------------------------------------------------------------------- /jest.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('jest').Config} */ 2 | export default { 3 | projects: ['packages/type-plus'], 4 | } 5 | -------------------------------------------------------------------------------- /old/checker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@type-plus/checker", 3 | "version": "0.0.0", 4 | "description": "Type-plus checker", 5 | "homepage": "https://github.com/unional/type-plus/tree/main/type-plus/packages/checker#readme", 6 | "bugs": { 7 | "url": "https://github.com/unional/type-plus/issues" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/unional/type-plus.git", 12 | "directory": "packages/checker" 13 | }, 14 | "license": "MIT", 15 | "author": { 16 | "name": "Homa Wong (unional)", 17 | "email": "homawong@gmail.com" 18 | }, 19 | "sideEffects": false, 20 | "type": "module", 21 | "dependencies": { 22 | "type-plus": "workspace:^" 23 | }, 24 | "devDependencies": { 25 | "@repobuddy/typescript": "^2.0.0", 26 | "@types/jest": "^29.5.13", 27 | "@types/node": "^18.11.11", 28 | "@unional/fixture": "^3.2.7", 29 | "depcheck": "^1.4.3", 30 | "npm-run-all2": "^8.0.0", 31 | "rimraf": "^6.0.0", 32 | "satisfier": "^5.4.2", 33 | "tersify": "^3.11.1" 34 | } 35 | } -------------------------------------------------------------------------------- /old/checker/src/type-checker/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/ban-types": "off", 4 | "@typescript-eslint/no-unsafe-assignment": "off", 5 | "@typescript-eslint/no-unsafe-return": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /old/checker/src/type-checker/AnyType.ts: -------------------------------------------------------------------------------- 1 | import { TypeAnalysis, Type, TypeSpec } from './types.js' 2 | 3 | export type Any = Type<'any', undefined> 4 | export namespace Any { 5 | export type Analysis = TypeAnalysis<'any'> 6 | } 7 | export const any: Any = { type: 'any', value: undefined } 8 | 9 | export const anySpec: TypeSpec = { 10 | type: any, 11 | toAnalysis: (options, value, _subject) => ({ type: 'any', value }), 12 | toNative: value => value 13 | } 14 | -------------------------------------------------------------------------------- /old/checker/src/type-checker/Tuple.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, test } from '@jest/globals' 2 | import { assertType } from '../assertion/assert_type.js' 3 | import type { Tuple } from './Tuple.js' 4 | 5 | describe('Find', () => { 6 | test('[] returns never', () => { 7 | assertType>(0 as never) 8 | }) 9 | test('[num], num returns num', () => { 10 | assertType>({ key: 1 }) 11 | }) 12 | test('[num,str] str returns str', () => { 13 | type Values = [{ type: 'a' }, { type: 'b' }, { type: 'c' }] 14 | assertType>({ type: 'c' }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /old/checker/src/type-checker/Tuple.ts: -------------------------------------------------------------------------------- 1 | export namespace Tuple { 2 | export type Head = T['length'] extends 0 ? never : T[0] 3 | export type Tail = T['length'] extends 0 4 | ? never 5 | : T extends [any, ...infer Tail] 6 | ? Tail 7 | : T 8 | 9 | export type FindByProp< 10 | Tuple extends Array<{ [K in Key]: any }>, 11 | Key extends string, 12 | Value 13 | > = FindByProp._FindByProp['result'] 14 | 15 | export namespace FindByProp { 16 | export type _FindByProp< 17 | Tuple extends Array<{ [K in Key]: any }>, 18 | Key extends string, 19 | Value 20 | > = Tuple['length'] extends 0 21 | ? { result: never } 22 | : { result: Value extends Tuple[0][Key] ? Tuple[0] : _FindByProp, Key, Value>['result'] } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /old/checker/src/type-checker/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unional/type-plus/07ccb2ae804589ce3a6f1a9ad5e52ed69c279fd3/old/checker/src/type-checker/index.ts -------------------------------------------------------------------------------- /old/checker/src/type-checker/typeChecker.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, test } from '@jest/globals' 2 | import { assertType } from '../index.js' 3 | import * as T from '../types/index.js' 4 | import { createTypeChecker } from './typeChecker.js' 5 | 6 | describe('check()', () => { 7 | test('bool', () => { 8 | const checker = createTypeChecker() 9 | 10 | const s: unknown = false 11 | if (checker.check({ strict: false, debug: false }, T.boolean.true, s)) { 12 | assertType(s) 13 | } 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /old/checker/src/type-checker/types.ts: -------------------------------------------------------------------------------- 1 | export type Checker = { 2 | type: Record 3 | } 4 | 5 | export type TypeSpec = Type, R = any> = { 6 | type: T 7 | toAnalysis( 8 | options: AnalysisOptions, 9 | value: T['value'], 10 | subject: unknown 11 | ): TypeAnalysis 12 | toNative(value: T['value']): R 13 | } 14 | 15 | export type AnalysisOptions = { strict: boolean; debug: boolean } 16 | 17 | export type Type = { 18 | type: T 19 | value: V 20 | } 21 | 22 | export type TypeAnalysis = { type: T; value?: V; fail?: true } 23 | -------------------------------------------------------------------------------- /old/checker/src/types/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "@typescript-eslint/ban-types": "off", 4 | "@typescript-eslint/no-unsafe-assignment": "off", 5 | "@typescript-eslint/no-unsafe-return": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /old/checker/src/types/Any.ts: -------------------------------------------------------------------------------- 1 | import { Type, TypeAnalysis } from './types.js' 2 | 3 | export type Any = Type<'any', undefined> 4 | export namespace Any { 5 | export type Analysis = TypeAnalysis<'any'> 6 | } 7 | export const any: Any = { type: 'any', value: undefined } 8 | -------------------------------------------------------------------------------- /old/checker/src/types/Null.ts: -------------------------------------------------------------------------------- 1 | import type { TypeAnalysis, Type } from './types.js' 2 | import { undef } from './Undefined.js' 3 | import { union } from './Union.js' 4 | 5 | export type Null = Type<'null', undefined> 6 | export namespace Null { 7 | export type Analysis = TypeAnalysis<'null'> 8 | } 9 | 10 | const any: Null = { type: 'null', value: undefined } 11 | 12 | export const nil = Object.assign({}, any, { 13 | optional: union.create(any, undef) 14 | }) 15 | -------------------------------------------------------------------------------- /old/checker/src/types/Record.ts: -------------------------------------------------------------------------------- 1 | import type { AllType } from './AllType.js' 2 | import type { TypeAnalysis, Type } from './types.js' 3 | import { undef, Undefined } from './Undefined.js' 4 | import { union, Union } from './Union.js' 5 | 6 | export type Record = Type<'record', Value> 7 | 8 | export namespace Record { 9 | export type Analysis = TypeAnalysis< 10 | 'record', 11 | Value 12 | > 13 | } 14 | 15 | function create(value: Value): Record { 16 | return { type: 'record', value } 17 | } 18 | 19 | export const record = { 20 | create, 21 | optional: { 22 | create(value: Value): Union<[Record, Undefined]> { 23 | return union.create(create(value), undef) as any 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /old/checker/src/types/Symbol.ts: -------------------------------------------------------------------------------- 1 | import type { TypeAnalysis, Type } from './types.js' 2 | import { undef } from './Undefined.js' 3 | import { union } from './Union.js' 4 | 5 | export type Symbol = Type<'symbol', Value> 6 | 7 | export namespace Symbol { 8 | export type Analysis = TypeAnalysis<'symbol'> 9 | } 10 | 11 | function create(value: Value): Symbol { 12 | return { type: 'symbol', value } 13 | } 14 | 15 | const any = create(undefined as unknown as string) 16 | 17 | export const symbol = Object.assign(any, { 18 | optional: union.create(any, undef) 19 | }) 20 | -------------------------------------------------------------------------------- /old/checker/src/types/Undefined.ts: -------------------------------------------------------------------------------- 1 | import type { TypeAnalysis, Type } from './types.js' 2 | 3 | export type Undefined = Type<'undefined', undefined> 4 | export namespace Undefined { 5 | export type Analysis = TypeAnalysis<'undefined'> 6 | } 7 | 8 | export const undef: Undefined = { type: 'undefined', value: undefined } 9 | -------------------------------------------------------------------------------- /old/checker/src/types/Unknown.ts: -------------------------------------------------------------------------------- 1 | import type { TypeAnalysis, Type } from './types.js' 2 | 3 | export type Unknown = Type<'unknown', undefined> 4 | 5 | export namespace Unknown { 6 | export type Analysis = TypeAnalysis<'unknown'> 7 | } 8 | 9 | export const unknown: Unknown = { type: 'unknown', value: undefined } 10 | -------------------------------------------------------------------------------- /old/checker/src/types/check.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | import { check } from './check.js' 3 | import * as T from './index.js' 4 | 5 | test('check without strict', () => { 6 | const t = T.tuple.create(T.string) 7 | expect(check({ strict: false }, t, ['a', 'b', 'c', 'd'])).toBe(true) 8 | }) 9 | 10 | test('check with strict', () => { 11 | const t = T.tuple.create(T.string) 12 | expect(check({ strict: true }, t, ['a', 'b', 'c', 'd'])).toBe(false) 13 | }) 14 | -------------------------------------------------------------------------------- /old/checker/src/types/check.ts: -------------------------------------------------------------------------------- 1 | import type { AllType } from './AllType.js' 2 | import { analyze } from './analyze.js' 3 | import type { Generate } from './Generate.js' 4 | import { getPlainAnalysisReport } from './getPlainAnalysisReport.js' 5 | 6 | /** 7 | * Checks if the specified `subject` against the `type`. 8 | * @return type guard (boolean). If the subject does not check to the type, 9 | * the detail report is available in `check.result`, 10 | * and you can get a string report using `check.getReport()` 11 | */ 12 | export function check( 13 | options: analyze.Options, 14 | type: T, 15 | subject: unknown 16 | ): subject is Generate { 17 | const result = (check.result = analyze(options, type, subject)) 18 | return !result.analysis.fail 19 | } 20 | 21 | /** 22 | * Analysis report. 23 | */ 24 | check.result = { analysis: {}, actual: undefined } as analyze.Result 25 | 26 | /** 27 | * Gets a simple report of the analysis. 28 | */ 29 | check.getReport = () => getPlainAnalysisReport(check.result) 30 | -------------------------------------------------------------------------------- /old/checker/src/types/conform.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | import { conform } from './conform.js' 3 | import * as T from './index.js' 4 | 5 | test('conform is strict', () => { 6 | const t = T.tuple.create(T.string) 7 | expect(conform(t, ['a', 'b', 'c', 'd'])).toBe(false) 8 | }) 9 | -------------------------------------------------------------------------------- /old/checker/src/types/optional.ts: -------------------------------------------------------------------------------- 1 | import { object } from './Object.js' 2 | import { any } from './Any.js' 3 | import { array } from './Array.js' 4 | import { boolean } from './Boolean.js' 5 | import { nil } from './Null.js' 6 | import { number } from './Number.js' 7 | import { string } from './String.js' 8 | import { symbol } from './Symbol.js' 9 | import { tuple } from './Tuple.js' 10 | import { undef } from './Undefined.js' 11 | import { unknown } from './Unknown.js' 12 | import { record } from './Record.js' 13 | // import { bigint } from './types/BigInt' 14 | 15 | export const optional = { 16 | any, 17 | array: array.optional, 18 | // bigint: bigint.optional, 19 | boolean: boolean.optional, 20 | null: nil.optional, 21 | number: number.optional, 22 | object: object.optional, 23 | record: record.optional, 24 | string: string.optional, 25 | symbol: symbol.optional, 26 | tuple: tuple.optional, 27 | undefined: undef, 28 | unknown 29 | } 30 | export const O = optional 31 | -------------------------------------------------------------------------------- /old/checker/src/types/required.ts: -------------------------------------------------------------------------------- 1 | import { object } from './Object.js' 2 | import { any } from './Any.js' 3 | import { array } from './Array.js' 4 | import { boolean } from './Boolean.js' 5 | import { nil } from './Null.js' 6 | import { number } from './Number.js' 7 | import { string } from './String.js' 8 | import { symbol } from './Symbol.js' 9 | import { tuple } from './Tuple.js' 10 | import { undef } from './Undefined.js' 11 | import { unknown } from './Unknown.js' 12 | import { record } from './Record.js' 13 | // import { bigint } from './types/BigInt' 14 | 15 | export const required = { 16 | any, 17 | array: array, 18 | // bigint: bigint, 19 | boolean: boolean, 20 | null: nil, 21 | number: number, 22 | object: object, 23 | record: record, 24 | string: string, 25 | symbol: symbol, 26 | tuple: tuple, 27 | undefined: undef, 28 | unknown 29 | } 30 | export const R = required 31 | -------------------------------------------------------------------------------- /old/checker/src/types/satisfy.ts: -------------------------------------------------------------------------------- 1 | import type { AllType } from './AllType.js' 2 | import { analyze } from './analyze.js' 3 | import type { Generate } from './Generate.js' 4 | import { getPlainAnalysisReport } from './getPlainAnalysisReport.js' 5 | 6 | /** 7 | * Checks if the specified `subject` satisfies the `type`. 8 | * `satisfy()` is the shortcut of `check({ strict: false }, ...)`. 9 | * @return type guard (boolean). If the subject does not satisfies the type, 10 | * the detail report is available in `satisfy.result`, 11 | * and you can get a string report using `satisfy.getReport()` 12 | */ 13 | export function satisfy(type: T, subject: unknown): subject is Generate { 14 | const result = (satisfy.result = analyze({ strict: false }, type, subject)) 15 | return !result.analysis.fail 16 | } 17 | 18 | /** 19 | * Analysis report. 20 | */ 21 | satisfy.result = { analysis: {} } as analyze.Result 22 | 23 | /** 24 | * Gets a simple report of the analysis. 25 | */ 26 | satisfy.getReport = () => getPlainAnalysisReport(satisfy.result) 27 | -------------------------------------------------------------------------------- /old/checker/src/types/types.ts: -------------------------------------------------------------------------------- 1 | export type Type = { 2 | type: T 3 | value: Value 4 | } 5 | 6 | export type TypeAnalysis = { 7 | type: T 8 | value?: V 9 | fail?: true 10 | } 11 | -------------------------------------------------------------------------------- /old/checker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repobuddy/typescript/tsconfig/monorepo", 3 | "compilerOptions": { 4 | "exactOptionalPropertyTypes": true, 5 | "outDir": "esm", 6 | "rootDir": "src", 7 | "verbatimModuleSyntax": true 8 | }, 9 | "include": ["src", "types"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/kind/.depcheckrc.yaml: -------------------------------------------------------------------------------- 1 | ignores: 2 | - '@repobuddy/*' 3 | - rimraf 4 | -------------------------------------------------------------------------------- /packages/kind/.gitignore: -------------------------------------------------------------------------------- 1 | # project folders 2 | /dist* 3 | -------------------------------------------------------------------------------- /packages/kind/src/fn.spec.ts: -------------------------------------------------------------------------------- 1 | import type { Eval, Id } from './index.js' 2 | 3 | // 'pass input to functions' 4 | type R = Eval<1, [Id]> 5 | const r: R = 1 6 | console.info(`${r} is 1`) 7 | -------------------------------------------------------------------------------- /packages/kind/src/fn.ts: -------------------------------------------------------------------------------- 1 | export interface Fn { 2 | input: [unknown, unknown] 3 | output: [unknown, unknown] 4 | } 5 | 6 | /** 7 | * Evaluate a series of functions on an input value. 8 | */ 9 | export type Eval = Pipe<[Input, unknown], Fns>[0] 10 | 11 | /** 12 | * Pipe input and error to a series of functions. 13 | * 14 | * @template Input - The initial input value for the pipeline. 15 | * @template Fns - The list of `Fn` functions to apply to the input. 16 | * @returns The final output value after applying all the functions in the pipeline. 17 | */ 18 | export type Pipe = Fns extends [ 19 | infer H extends Fn, 20 | ...infer Rest extends Fn[], 21 | ] 22 | ? Pipe< 23 | (H & { 24 | input: Input 25 | })['output'], 26 | Rest 27 | > 28 | : Input 29 | -------------------------------------------------------------------------------- /packages/kind/src/id.ts: -------------------------------------------------------------------------------- 1 | import type { Fn } from './fn.js' 2 | 3 | /** 4 | * Identity function. 5 | */ 6 | export interface Id extends Fn { 7 | output: this['input'] 8 | } 9 | -------------------------------------------------------------------------------- /packages/kind/src/index.ts: -------------------------------------------------------------------------------- 1 | export type * from './fn.js' 2 | export type * from './id.js' 3 | -------------------------------------------------------------------------------- /packages/kind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repobuddy/typescript/tsconfig/monorepo", 3 | "compilerOptions": { 4 | "composite": false, 5 | "outDir": "dist", 6 | "rootDir": "src", 7 | "verbatimModuleSyntax": true 8 | }, 9 | "include": ["src", "types"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/type-plus/.depcheckrc.yaml: -------------------------------------------------------------------------------- 1 | ignores: 2 | - '@jest/globals' 3 | - '@repobuddy/*' 4 | - '@size-limit/esbuild-why' 5 | - '@size-limit/preset-small-lib' 6 | - '@types/node' 7 | - eslint-* 8 | - jest-* 9 | - rimraf 10 | - ts-jest 11 | - tslib 12 | -------------------------------------------------------------------------------- /packages/type-plus/.gitignore: -------------------------------------------------------------------------------- 1 | # project folders 2 | /.turbo 3 | /cjs 4 | /dist* 5 | /docs 6 | /esm 7 | /lib* 8 | /out* 9 | /tslib 10 | *.tsbuildinfo 11 | -------------------------------------------------------------------------------- /packages/type-plus/.repobuddy.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@repobuddy/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/type-plus/.size-limit.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "esm", 4 | "path": "./esm/index.js", 5 | "limit": "15 kb" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /packages/type-plus/fixtures/eslint/cases/empty.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/type-plus/jest.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('jest').Config} */ 2 | export default { 3 | preset: '@repobuddy/jest/presets/ts-esm-watch', 4 | roots: ['/src'], 5 | } 6 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/$resolve_options.ts: -------------------------------------------------------------------------------- 1 | import type { $InferError } from './errors/$infer_error.js' 2 | 3 | /** 4 | * 🧰 *type util* 5 | * 6 | * Resolve options to the first non `unknown` value. 7 | * 8 | * The `Values` are assumed to be a tuple with at least one value. 9 | * These checks are not performed for performance considerations. 10 | */ 11 | export type $ResolveOptions = V extends [infer T] 12 | ? T 13 | : V extends [infer T, ...infer U] 14 | ? [T, unknown] extends [unknown, T] 15 | ? $ResolveOptions 16 | : [T] extends [undefined] 17 | ? $ResolveOptions 18 | : T 19 | : $InferError<'cannot [infer T, ...infer U] from', V> 20 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/branch/$branch.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it } from '@jest/globals' 2 | import type { $Any, $Branch, $BranchOptions, $Type, $Unknown } from '../../index.js' 3 | import { testType } from '../../index.js' 4 | 5 | it('create a branch type with property name', () => { 6 | type $Then = $Branch<'$then'> 7 | testType.equal<$Then[$Type.$ValueKey], '$then'>(true) 8 | }) 9 | 10 | it('the property name must start with $', () => { 11 | // @ts-expect-error 12 | type _$DoesNotWork = $Branch<'nope'> 13 | }) 14 | 15 | describe('$BranchOptions', () => { 16 | it('creates branch options with single branch', () => { 17 | testType.equal<$BranchOptions<$Any>, { $any: $Any }>(true) 18 | }) 19 | 20 | it('creates branch options with multiple branches', () => { 21 | testType.equal< 22 | $BranchOptions<$Any | $Unknown>, 23 | { 24 | $any: $Any 25 | $unknown: $Unknown 26 | } 27 | >(true) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/branch/$branch.ts: -------------------------------------------------------------------------------- 1 | import type { $Type } from '../$type.js' 2 | 3 | /** 4 | * 🧰 *type util* 5 | * 6 | * Create a branch type. 7 | * 8 | * @typeparam P the property name for the branch. 9 | * 10 | * @example 11 | * ```ts 12 | * type $Then = $Branch<'$then'> 13 | * type $Any = $Branch<'$any'> 14 | */ 15 | export type $Branch

= $Type<'branch', P> 16 | 17 | /** 18 | * 🧰 *type util* 19 | * 20 | * Define the branch options of the specified branches. 21 | * 22 | * ```ts 23 | * type $YourOptions = $BranchOptions<$Then | $Else> // { $then: $Then, $else: $Else } 24 | * ``` 25 | */ 26 | export type $BranchOptions<$B extends $Branch> = { 27 | [k in $B[$Type.$ValueKey]]: $B extends { _$value: k } ? $B : never 28 | } 29 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/branch/$input_options.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type $Any, type $InputOptions, type $Unknown, testType } from '../../index.js' 4 | 5 | it('creates branch options with single branch', () => { 6 | testType.equal<$InputOptions<$Any>, { $any?: unknown }>(true) 7 | }) 8 | 9 | it('creates branch options with multiple branches', () => { 10 | testType.equal< 11 | $InputOptions<$Any | $Unknown>, 12 | { 13 | $any?: unknown 14 | $unknown?: unknown 15 | } 16 | >(true) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/branch/$input_options.ts: -------------------------------------------------------------------------------- 1 | import type { $Type } from '../$type.js' 2 | import type { $Branch } from './$branch.js' 3 | 4 | /** 5 | * 🧰 *type util* 6 | * 7 | * Define branch input options. 8 | */ 9 | export type $InputOptions<$B extends $Branch> = { [k in $B[$Type.$ValueKey]]?: unknown } 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/distributive/$distributive.ts: -------------------------------------------------------------------------------- 1 | import type { $ResolveOptions } from '../$resolve_options.js' 2 | import type { $InputOptions } from '../branch/$input_options.js' 3 | import type { $Else, $Then } from '../branch/$selection.js' 4 | 5 | export namespace $Distributive { 6 | /** 7 | * Options for controlling if the type is distributive. 8 | */ 9 | export type Options = { 10 | distributive?: boolean | undefined 11 | } 12 | 13 | /** 14 | * Default options for `distributive` behavior. 15 | * 16 | * By default it is `true`. 17 | */ 18 | export type Default = { 19 | distributive: true 20 | } 21 | 22 | /** 23 | * Parse the options for `distributive`. 24 | */ 25 | export type Parse<$Options extends Options, $O extends $InputOptions<$Then | $Else> = {}> = $ResolveOptions< 26 | [$Options['distributive'], Default['distributive']] 27 | > extends true 28 | ? '$then' extends keyof $O 29 | ? $O['$then'] 30 | : true 31 | : '$else' extends keyof $O 32 | ? $O['$else'] 33 | : false 34 | } 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/distributive/readme.md: -------------------------------------------------------------------------------- 1 | # Distributive 2 | 3 | Distributive in TypeScript means each type in a union type is treated as a separate type during type calculation. 4 | 5 | Each type in a union type is passed through the type calculation independently. 6 | 7 | ## [`$Distributive.Options`](./$distributive.ts) 8 | 9 | 🏷️ **since 8.0.0** 10 | 11 | Many types in this library are distributive. 12 | 13 | `$Distributive.Options` is a type option indicating the type support distributive and non-distributive mode. 14 | 15 | ## [`$Distributive.Default`](./$distributive.ts) 16 | 17 | 🏷️ **since 8.0.0** 18 | 19 | `$Distributive.Default` is the default value for [`$Distributive.Options`](#distributiveoptions). 20 | `distributive` is `true` by default. 21 | 22 | ## [`$Distributive.Parse`](./$distributive.ts) 23 | 24 | 🏷️ **since 8.0.0** 25 | 26 | `$Distributive.Parse<$Option, $O>` checks if a type `$Option` enables distributive or not. 27 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/errors/$error.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type $Error, type $Type, testType } from '../../index.js' 4 | 5 | it('requires message', () => { 6 | type R = $Error<'some message'> 7 | testType.equal(true) 8 | testType.equal(true) 9 | 10 | testType.equal(true) 11 | }) 12 | 13 | it('can provide type', () => { 14 | type R = $Error<'some message', number> 15 | 16 | testType.equal(true) 17 | testType.equal(true) 18 | testType.equal(true) 19 | 20 | testType.equal(true) 21 | testType.equal(true) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/errors/$error.ts: -------------------------------------------------------------------------------- 1 | import type { $Type } from '../$type.js' 2 | 3 | /** 4 | * 🧰 *type util* 5 | * 6 | * A type-level error. 7 | * 8 | * This is analogous to the `Error` class in JavaScript. 9 | * 10 | * It can be used in type-level programming to represent an error with a message. 11 | * 12 | * @example 13 | * ```ts 14 | * type T = $Error<'error message'> 15 | * type T = $Error<'error message', number> 16 | * ``` 17 | * 18 | * @since 8.0.0 19 | */ 20 | export type $Error = M extends any ? $Type<'error', { message: M; type: T }> : never 21 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/errors/$infer_error.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type $InferError, type $Type, testType } from '../../index.js' 4 | 5 | it('can specify message only', () => { 6 | type R = $InferError<'some message'> 7 | 8 | testType.equal(true) 9 | testType.equal(true) 10 | }) 11 | 12 | it('can specify type value', () => { 13 | type R = $InferError<'some message', { a: { b: { c: { d: { e: { f: { g: number } } } } } } }> 14 | testType.equal(true) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/errors/$infer_error.ts: -------------------------------------------------------------------------------- 1 | import type { $Error } from './$error.js' 2 | 3 | /** 4 | * An error to indicate unexpected failure when inferring type. 5 | * 6 | * In your type, 7 | * you can use `T extends infer U extends V` to specify the type of the inferred type `U`. 8 | * 9 | * But doing so means you have to do an extra conditional type. 10 | * 11 | * This type can be use in the else case to indicate unexpected failure when inferring type. 12 | * 13 | * @example 14 | * ```ts 15 | * type F = T extends infer U extends V 16 | * ? ...your type logic... 17 | * : InferError<'some message', T> 18 | * ``` 19 | * 20 | * @since 8.0.0 21 | */ 22 | export type $InferError = M extends any ? $Error<`Unable to infer: ${M}`, T> : never 23 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/errors/failed.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type Failed, type FailedT, testType } from '../../index.js' 4 | 5 | it('shows error message (inspect by hover over it)', () => { 6 | type R = Failed<'error message'> 7 | 8 | testType.equal>(true) 9 | }) 10 | 11 | it('shows error message with type', () => { 12 | type R = FailedT<'type should be', number | string> 13 | 14 | testType.equal>(true) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/exact/$exact.ts: -------------------------------------------------------------------------------- 1 | import type { $ResolveOptions } from '../$resolve_options.js' 2 | import type { $InputOptions } from '../branch/$input_options.js' 3 | import type { $Else, $Then } from '../branch/$selection.js' 4 | 5 | export namespace $Exact { 6 | /** 7 | * Options for controlling if the type perform exact comparison. 8 | */ 9 | export type Options = { 10 | exact?: boolean | undefined 11 | } 12 | 13 | /** 14 | * Default options for `exact` behavior. 15 | * 16 | * By default it is `false`. 17 | */ 18 | export type Default = { 19 | exact: false 20 | } 21 | 22 | /** 23 | * Parse the options for `exact`. 24 | */ 25 | export type Parse<$Options extends Options, $O extends $InputOptions<$Then | $Else> = {}> = $ResolveOptions< 26 | [$Options['exact'], Default['exact']] 27 | > extends true 28 | ? '$then' extends keyof $O 29 | ? $O['$then'] 30 | : true 31 | : '$else' extends keyof $O 32 | ? $O['$else'] 33 | : false 34 | } 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/exact/readme.md: -------------------------------------------------------------------------------- 1 | # Exact comparison 2 | 3 | Types in TypeScript is a Set, they can contain subtypes. 4 | 5 | For example, The `string` type can have string literals, 6 | `boolean` can be `true` or `false`, etc. 7 | 8 | When comparing two types, we can compare them loosely or strictly. 9 | 10 | ## [`$Exact.Options`](./$exact.ts) 11 | 12 | 🏷️ **since 8.0.0** 13 | 14 | `$Exact.Options` is a type option indicating the type support exact comparison. 15 | 16 | ## [`$Exact.Default`](./$exact.ts) 17 | 18 | 🏷️ **since 8.0.0** 19 | 20 | `$Exact.Default` is the default value for [`$Exact.Options`](#exactoptions). 21 | `exact` is `false` by default. 22 | 23 | ## [`$Exact.Parse`](./$exact.ts) 24 | 25 | 🏷️ **since 8.0.0** 26 | 27 | `$Exact.Parse<$Option, $O>` checks if a type `$Option` enables exact comparison or not. 28 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/readme.md: -------------------------------------------------------------------------------- 1 | # Type-level programming 2 | 3 | This folder contains utility types for type-level programming. 4 | 5 | These utility types are not meant to be used directly. 6 | They are primarily used to build custom types. 7 | 8 | Most of these types are named with a `$` prefix to distinguish them from regular types. 9 | 10 | ## [`$Type`](./$type.ts) 11 | 12 | 🏷️ **since 8.0.0** 13 | 14 | `$Type` is a branded type to define unique types for type-level programming. 15 | 16 | It supports all primitive types and object types. 17 | 18 | When using object types, the type intersect with the specified type to give easy access to its properties. 19 | 20 | Internally, it uses the properties `_$type` and `_$value` to store the type and value. 21 | The type you provide should avoid specifying these properties. 22 | 23 | If needed, use `$O: { bare: true }` to avoid the intersection. 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/special/$any.ts: -------------------------------------------------------------------------------- 1 | import type { $Branch } from '../branch/$branch.js' 2 | 3 | /** 4 | * Branch selector for type `any`. 5 | */ 6 | export type $Any = $Branch<'$any'> 7 | 8 | declare const $any: '$any' 9 | 10 | export namespace $Any { 11 | export type $Key = '$any' 12 | /** 13 | * Options to specifically handles the `any` type. 14 | * 15 | * @example 16 | * ```ts 17 | * type YourType = ... 18 | * ``` 19 | */ 20 | export type $Options = { [$any]?: unknown } 21 | 22 | /** 23 | * Branch option to specifically handles the `any` type. 24 | * 25 | * Use this to finely customize the behavior of your type. 26 | * 27 | * ```ts 28 | * type YourType = ... 29 | * 30 | * type R = YourType extends $Any ? HandleAny : HandleOthers 31 | * ``` 32 | */ 33 | export type $Branch = { [$any]: $Any } 34 | } 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/special/$never.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import { type $Never, type $NotNever, testType } from '../../index.js' 3 | 4 | it('$Never and $NotNever is not the same', () => { 5 | testType.equal<$Never, $NotNever>(false) 6 | }) 7 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/special/$unknown.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import { type $Type, type $Unknown, testType } from '../../index.js' 3 | 4 | it('is a unique branch', () => { 5 | testType.canAssign<$Type<'branch', 'something else'>, $Unknown>(false) 6 | }) 7 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/special/$unknown.ts: -------------------------------------------------------------------------------- 1 | import type { $Branch } from '../branch/$branch.js' 2 | 3 | /** 4 | * Branch selector for type `unknown`. 5 | */ 6 | export type $Unknown = $Branch<'$unknown'> 7 | 8 | declare const $unknown: '$unknown' 9 | 10 | export namespace $Unknown { 11 | export type $Key = '$unknown' 12 | /** 13 | * Options to specifically handles the `unknown` type. 14 | * 15 | * @example 16 | * ```ts 17 | * type YourType = ... 18 | * ``` 19 | */ 20 | export type $Options = { [$unknown]?: unknown } 21 | 22 | /** 23 | * Branch option to specifically handles the `unknown` type. 24 | * 25 | * Use this to finely customize the behavior of your type. 26 | * 27 | * @example 28 | * ```ts 29 | * type YourType = ... 30 | * 31 | * type R = YourType extends $Unknown ? HandleUnknown : HandleOthers 32 | * ``` 33 | */ 34 | export type $Branch = { [$unknown]: $Unknown } 35 | } 36 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/utils/$merge_options.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import { type $MergeOptions, testType } from '../../index.js' 3 | 4 | it('overrides', () => { 5 | testType.equal<$MergeOptions<{ $any: 1 }, {}>, { $any: 1 }>(true) 6 | testType.equal<$MergeOptions<{ $any: 1 }, { $any: 2 }>, { $any: 2 }>(true) 7 | testType.equal<$MergeOptions<{ $any: 1; $else: 2 }, { $any: 2 }>, { $any: 2; $else: 2 }>(true) 8 | }) 9 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/utils/$merge_options.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Merge type options. 3 | * 4 | * This is used in the type to merge the user provided options with the default options. 5 | * 6 | * @typeparam $O - The type of the options, typically provided by the user. 7 | * @typeparam $P - The type of the default options. 8 | */ 9 | export type $MergeOptions<$O extends Record, $P extends { [k in keyof $O]?: unknown }> = { 10 | [k in Exclude]: $O[k] 11 | } & $P 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/$type/utils/readme.md: -------------------------------------------------------------------------------- 1 | # Type-level utilities 2 | 3 | This folder contains utility types for the types in type-level programming. 4 | 5 | ## [`$MergeOptions`](./merge_options.ts) 6 | 7 | 🏷️ **since 8.0.0** 8 | 9 | `$MergeOptions<$O, $P>` merges two options types `$O` and `$P`. 10 | This is used in the type to merge the user provided options with the default options. 11 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array.entries.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import { type ArrayPlus, testType } from '../index.js' 3 | 4 | it('gets Array<[number, T]> for array', () => { 5 | testType.equal, Array<[number, string]>>(true) 6 | testType.equal>, Array<[number, string | number]>>(true) 7 | }) 8 | 9 | it('returns [[0, T1], [1, T2],...[n, Tn]] for tuple', () => { 10 | testType.equal, []>(true) 11 | testType.equal, [[0, 1], [1, 2], [2, 3]]>(true) 12 | }) 13 | 14 | it('supports readonly array', () => { 15 | testType.equal, [[0, 1], [1, 2], [2, 3]]>(true) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array.entries.ts: -------------------------------------------------------------------------------- 1 | import type { IsTuple } from '../tuple/is_tuple.js' 2 | /** 3 | * Returns an array of key-value pairs for every entry in the array or tuple. 4 | * 5 | * Note that this is not the same as `Array.entries(A)`, 6 | * which returns an iterable interator. 7 | * 8 | * @example 9 | * ```ts 10 | * ArrayPlus.Entries> // Array<[number, string | number]> 11 | * ArrayPlus.Entries<[1, 2, 3]> // [[0, 1], [1, 2], [2, 3]] 12 | * ``` 13 | */ 14 | export type Entries = IsTuple< 15 | A, 16 | { 17 | $then: Entries.Device 18 | $else: A extends Array ? Array<[number, T]> : never 19 | } 20 | > 21 | 22 | export namespace Entries { 23 | export type Device = A['length'] extends 0 24 | ? R 25 | : A extends readonly [...infer F, infer N] 26 | ? Device 27 | : never 28 | } 29 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array.find_last.ts: -------------------------------------------------------------------------------- 1 | import type { IsTuple } from '../tuple/is_tuple.js' 2 | 3 | /** 4 | * 🦴 *utilities* 5 | * 6 | * Gets the last type in the array or tuple that matches the `Criteria`. 7 | * 8 | * If the `Criteria` is not met, it will return `never'. 9 | * 10 | * For `Array`, it will return `T | undefined` if `T` satisfies `Criteria`. 11 | * 12 | * @example 13 | * ```ts 14 | * ArrayPlus.Find, number> // 1 | 2 | undefined 15 | * 16 | * ArrayPlus.Find<[true, 123, 'x', 321], number> // 321 17 | * ``` 18 | */ 19 | export type FindLast = IsTuple< 20 | A, 21 | { 22 | $then: A['length'] extends 0 23 | ? never 24 | : A extends readonly [...infer Heads, infer Last] 25 | ? Last extends Criteria 26 | ? Last 27 | : FindLast 28 | : never 29 | $else: A extends Readonly> ? (T extends Criteria ? T | undefined : never) : never 30 | } 31 | > 32 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array.reverse.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import type { ArrayPlus } from '../index.js' 4 | import { testType } from '../index.js' 5 | 6 | it('should reverse array to itself', () => { 7 | testType.equal, string[]>(true) 8 | testType.equal>, Array>(true) 9 | }) 10 | 11 | it('should reverse empty tuple [] to []', () => { 12 | testType.equal, []>(true) 13 | }) 14 | 15 | it('should reverse [1, 2, 3] to [3, 2, 1]', () => { 16 | testType.equal, [3, 2, 1]>(true) 17 | }) 18 | 19 | it('supports readonly array', () => { 20 | testType.equal, readonly string[]>(true) 21 | testType.equal, readonly [3, 2, 1]>(true) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array.reverse.ts: -------------------------------------------------------------------------------- 1 | import type { IsReadonly } from './array_plus.is_readonly.js' 2 | 3 | /* 4 | * Reverses the order of elements in the array or tuple. 5 | * 6 | * @example 7 | * ```ts 8 | * Reverse> // Array 9 | * 10 | * Reverse<[1, 2, 3]> // [3, 2, 1] 11 | * ``` 12 | * 13 | * @param T The array type to reverse. 14 | * @returns The reversed array type. 15 | */ 16 | export type Reverse = Reverse._ extends infer R 17 | ? IsReadonly extends true 18 | ? Readonly 19 | : R 20 | : never 21 | 22 | export namespace Reverse { 23 | export type _ = A extends readonly [infer First, ...infer Rest] 24 | ? [..._, First] 25 | : A 26 | } 27 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.common_prop_keys.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import { type ArrayPlus, testType } from '../index.js' 3 | 4 | it('never returns never', () => { 5 | testType.equal, never>(true) 6 | }) 7 | 8 | it('can override never case', () => { 9 | testType.equal, 1>(true) 10 | }) 11 | 12 | it('accepts readonly array', () => { 13 | testType.equal, 'a'>(true) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.concat.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 🦴 *utilities 3 | * 💀 *deprecated* Will be available only as `ArrayPlus.Concat` in the next version 4 | * 5 | * Concats two arrays or tuples. 6 | * 7 | * alias of: `[...A, ...B]` 8 | * 9 | * @alias ArrayPlus.Concat 10 | * 11 | * ```ts 12 | * type R = Concat<[1], [2, 3]> // [1, 2, 3] 13 | * ``` 14 | */ 15 | export type Concat, B extends Readonly> = [...A, ...B] 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.drop_match.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * ⚗️ *transform* 3 | */ 4 | export type DropMatch>, Criteria> = A[0] extends Criteria 5 | ? never[] 6 | : undefined extends Criteria 7 | ? null extends Criteria 8 | ? Array> 9 | : Array> 10 | : null extends Criteria 11 | ? Array> 12 | : Criteria extends A[0] 13 | ? Array> 14 | : A[0] extends Criteria 15 | ? A 16 | : Array> 17 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.element_match.unit.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import { testType } from '../index.js' 3 | import type { ElementMatch } from './array_plus.js' 4 | 5 | it('can disable widen support', () => { 6 | testType.equal, never>(true) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.filter.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type ArrayPlus, testType } from '../index.js' 4 | 5 | it('returns never for never case', () => { 6 | testType.equal, never>(true) 7 | }) 8 | 9 | it('can override never case', () => { 10 | testType.equal, 1>(true) 11 | }) 12 | 13 | it('can override not array case', () => { 14 | testType.equal, 1>(true) 15 | }) 16 | 17 | it('filter type within the array matching the criteria', () => { 18 | testType.equal, string>, string[]>(true) 19 | }) 20 | 21 | it('defaults to match true', () => { 22 | testType.equal>, true[]>(true) 23 | testType.equal>, true[]>(true) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.find.ts54.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | import { testType } from '../index.js' 3 | 4 | test('behavior of array.find()', () => { 5 | const array = [1, 2, '3'] 6 | const r = array.find((x) => typeof x === 'number') 7 | testType.equal(true) 8 | }) 9 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.find.ts55.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | import { testType } from '../index.js' 3 | 4 | test('behavior of array.find()', () => { 5 | const array = [1, 2, '3'] 6 | const r = array.find((x) => typeof x === 'number') 7 | testType.equal(true) 8 | }) 9 | 10 | test('behavior of array.entries()', () => { 11 | const array = [1, 2, '3'] 12 | const entries = array.entries() 13 | testType.equal>(true) 14 | }) 15 | 16 | test('behavior of tuple.entries()', () => { 17 | const tuple = [1, 2, '3'] as const 18 | const entries = tuple.entries() 19 | testType.equal>(true) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.find.ts56.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | import { testType } from '../index.js' 3 | 4 | test('behavior of array.entries()', () => { 5 | const array = [1, 2, '3'] 6 | const entries = array.entries() 7 | testType.equal>(true) 8 | }) 9 | 10 | test('behavior of tuple.entries()', () => { 11 | const tuple = [1, 2, '3'] as const 12 | const entries = tuple.entries() 13 | testType.equal>(true) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.is_index_out_of_bound.ts: -------------------------------------------------------------------------------- 1 | import type { IsNever } from '../never/is_never.js' 2 | import type { IndexAt } from './array_plus.index_at.js' 3 | 4 | /** 5 | * 🎭 *predicate* 6 | * 7 | * Is `N` an out of bound index of `A`. 8 | * 9 | * @example 10 | * ```ts 11 | * type R = IsIndexOutOfBound<[1], 0> // false 12 | * type R = IsIndexOutOfBound<[1], -1> // false 13 | * 14 | * type R = IsIndexOutOfBound<[1], 1> // true 15 | * type R = IsIndexOutOfBound<[1], -2> // true 16 | * ``` 17 | */ 18 | export type IsIndexOutOfBound = IsNever< 19 | IndexAt, 20 | { 21 | $then: Then 22 | $else: Else 23 | } 24 | > 25 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.pad_start.ts: -------------------------------------------------------------------------------- 1 | import type { CanAssign } from '../index.js' 2 | import type { CreateTuple } from '../tuple/create_tuple.js' 3 | import type { UnionOfValues } from './union_of_values.js' 4 | 5 | export type PadStart = MaxLength extends 0 6 | ? A 7 | : CanAssign> extends true 8 | ? A 9 | : PadStart<[...CreateTuple, ...A], MaxLength, PadWith> 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import { type ArrayPlus, testType } from '../index.js' 3 | 4 | it('exports all array types and utils', () => { 5 | testType.equal, 3>(true) 6 | testType.equal, [1, 2, 3, 4]>(true) 7 | testType.equal, 2>(true) 8 | testType.equal, false>(true) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/array_plus.ts: -------------------------------------------------------------------------------- 1 | export type { At } from './array.at.js' 2 | export type { Entries } from './array.entries.js' 3 | export type { FindLast } from './array.find_last.js' 4 | export type { Reverse } from './array.reverse.js' 5 | export type { Some } from './array.some.js' 6 | export type { CommonPropKeys } from './array_plus.common_prop_keys.js' 7 | export type { Concat } from './array_plus.concat.js' 8 | export type { DropMatch } from './array_plus.drop_match.js' 9 | export type { ElementMatch } from './array_plus.element_match.js' 10 | export type { Filter } from './array_plus.filter.js' 11 | export type { Find } from './array_plus.find.js' 12 | export type { IndexAt } from './array_plus.index_at.js' 13 | export type { IsIndexOutOfBound } from './array_plus.is_index_out_of_bound.js' 14 | export type { IsReadonly } from './array_plus.is_readonly.js' 15 | export type { PadStart } from './array_plus.pad_start.js' 16 | export type { SplitAt } from './array_plus.split_at.js' 17 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/head.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type Head, testType } from '../index.js' 4 | 5 | it('returns never for empty tuple', () => { 6 | testType.equal, never>(true) 7 | }) 8 | 9 | it('can override never case', () => { 10 | testType.equal, 1>(true) 11 | }) 12 | 13 | it('gets the type of an array', () => { 14 | testType.equal, string>(true) 15 | }) 16 | 17 | it('returns the first type of a tuple', () => { 18 | testType.equal, 1>(true) 19 | }) 20 | 21 | it('returns never for empty tuple', () => { 22 | testType.equal, never>(true) 23 | }) 24 | 25 | it('can override empty tuple case', () => { 26 | testType.equal, undefined>(true) 27 | }) 28 | 29 | it('support readonly tuple', () => { 30 | testType.equal, string>(true) 31 | testType.equal, 1>(true) 32 | }) 33 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/intersect_of_props.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type IntersectOfProps, testType } from '../index.js' 4 | 5 | it('gets property from single value tuple', () => { 6 | type S = [{ a: number }] 7 | type A = IntersectOfProps 8 | 9 | testType.equal(true) 10 | }) 11 | 12 | it('gets property from multiple values', () => { 13 | type S = [{ a: { x: number } }, { a: { y: string } }] 14 | type A = IntersectOfProps 15 | testType.equal(true) 16 | }) 17 | 18 | it('gets property from array', () => { 19 | testType.equal, 'a'>, number | string>(true) 20 | }) 21 | 22 | it('support readonly array', () => { 23 | type A = IntersectOfProps 24 | testType.equal(true) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/intersect_of_props.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from '../object/index.js' 2 | import type { Tail } from '../tuple/tail.js' 3 | 4 | /** 5 | * 🦴 *utilities* 6 | * 7 | * Gets the intersect of properties of the elements in `A`. 8 | */ 9 | export type IntersectOfProps[], P extends KeyTypes> = number extends A['length'] 10 | ? A[0][P] 11 | : A['length'] extends 0 12 | ? never 13 | : A['length'] extends 1 14 | ? A[0][P] 15 | : A[0][P] & IntersectOfProps, P> 16 | 17 | /** 18 | * Gets the intersect of properties of the elements in `A` 19 | * This will be deprecated in 4.0. Please use IntersectOfProps instead. 20 | */ 21 | export type MapToProp[], P extends KeyTypes> = IntersectOfProps 22 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/literal_array.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { literalArray, testType } from '../index.js' 4 | 5 | test('entries in array are restricted to the input literals', () => { 6 | const actual = literalArray('a', 'b') 7 | 8 | testType.equal>(true) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/literal_array.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from '../object/index.js' 2 | 3 | /** 4 | * 🦴 *utilities* 5 | * 6 | * return an array whose items are restricted to the provided literals. 7 | */ 8 | export function literalArray(...entries: T[]): T[] { 9 | return entries 10 | } 11 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/reduce_while.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals' 2 | 3 | import { reduceWhile } from '../index.js' 4 | 5 | describe('reduceWhile()', () => { 6 | test('with true predicate act like normal reduce', () => { 7 | const array = ['a', 'b', 'c'] 8 | const params: any[] = [] 9 | 10 | array.reduce((p, v, i, a) => (params.push([p, v, i, a]), (p += v)), '') 11 | expect( 12 | reduceWhile( 13 | () => true, 14 | (p, v, i, a) => { 15 | expect([p, v, i, a]).toEqual(params.shift()) 16 | return (p += v) 17 | }, 18 | '', 19 | array, 20 | ), 21 | ).toEqual('abc') 22 | }) 23 | test('terminate early with predicate', () => { 24 | expect( 25 | reduceWhile( 26 | (p, v) => p < v, 27 | (p, v) => (p += v), 28 | -2, 29 | [3, 2, 1], 30 | ), 31 | ).toEqual(3) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/reduce_while.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 🦴 *utilities* 3 | * 4 | * `reduce()` with predicate for early termination. 5 | * A simple version of the same function in the `ramda` package. 6 | */ 7 | export function reduceWhile( 8 | predicate: (acc: R, currentValue: T) => boolean, 9 | callbackfn: (previousValue: R, currentValue: T, currentIndex: number, array: T[]) => R, 10 | initialValue: R, 11 | array: T[], 12 | ) { 13 | let acc = initialValue 14 | for (let i = 0; i < array.length; i++) { 15 | const value = array[i]! 16 | if (!predicate(acc, value)) return acc 17 | acc = callbackfn(acc, value, i, array) 18 | } 19 | return acc 20 | } 21 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/reverse.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type Reverse, testType } from '../index.js' 4 | 5 | test('empty array gets itself', () => { 6 | testType.equal, []>(true) 7 | }) 8 | 9 | test('array type gets itself', () => { 10 | testType.equal, string[]>(true) 11 | }) 12 | 13 | test('single element array gets itself', () => { 14 | testType.equal, [1]>(true) 15 | }) 16 | 17 | test('multi elements', () => { 18 | testType.equal, [4, 3, 2, 1]>(true) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/reverse.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * ⚗️ *transform* 3 | * 4 | * reverses the order of `A`. 5 | */ 6 | export type Reverse = number extends A['length'] 7 | ? A 8 | : A['length'] extends 0 9 | ? A 10 | : A['length'] extends 1 11 | ? A 12 | : A extends [any, ...infer T] 13 | ? T extends any[] 14 | ? [...Reverse, A[0]] 15 | : never 16 | : never 17 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/union_of_props.spec.ts: -------------------------------------------------------------------------------- 1 | import { it, test } from '@jest/globals' 2 | 3 | import { type UnionOfProps, testType } from '../index.js' 4 | 5 | test('get property from single value tuple', () => { 6 | type S = [{ a: number }] 7 | type A = UnionOfProps 8 | testType.equal(true) 9 | }) 10 | 11 | test('get property from multiple values', () => { 12 | type S = [{ a: 'a' }, { a: 'b' }] 13 | type A = UnionOfProps 14 | testType.equal(true) 15 | }) 16 | 17 | it('supports readonly array', () => { 18 | testType.equal, number>(true) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/union_of_props.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from '../object/index.js' 2 | import type { Tail } from '../tuple/tail.js' 3 | 4 | /** 5 | * Gets the union of properties of the elements in `A` 6 | */ 7 | export type UnionOfProps[], P extends KeyTypes> = A['length'] extends 0 8 | ? never 9 | : A['length'] extends 1 10 | ? A[0][P] 11 | : A[0][P] | UnionOfProps, P> 12 | 13 | /** 14 | * Gets the union of properties in the element of `A` 15 | * This will be deprecated in 4.0. Please use UnionOfProps instead. 16 | */ 17 | export type PropUnion[], P extends KeyTypes> = UnionOfProps 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/union_of_values.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type UnionOfValues, testType } from '../index.js' 4 | 5 | it('returns type T of Array', () => { 6 | testType.equal, string>(true) 7 | testType.equal>, string | number>(true) 8 | }) 9 | 10 | it('returns the union of values of an tuple', () => { 11 | testType.equal, string | boolean>(true) 12 | }) 13 | 14 | it('returns literal types from tuple', () => { 15 | testType.equal, 'a' | 1 | true>(true) 16 | }) 17 | 18 | it('preserves union types', () => { 19 | const t: ['a' | 'b', number, boolean] = ['a', 1, true] 20 | type R = UnionOfValues 21 | testType.equal(true) 22 | }) 23 | 24 | it('supports readonly tuple', () => { 25 | testType.equal, string>(true) 26 | testType.equal, string | boolean>(true) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/type-plus/src/array/union_of_values.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets the union of value types in `A` 3 | */ 4 | export type UnionOfValues = A extends Readonly> ? E : never 5 | 6 | // alternative implementation 7 | // export type UnionOfValues = (A)[number] 8 | // from: https://twitter.com/anveio/status/1615140804816928769?s=20&t=wrudiqV94A11CSl19N6Viw 9 | 10 | /** 11 | * Gets the union of value types in `A` 12 | * @deprecated Please use `UnionOfValues` instead. 13 | */ 14 | export type ArrayValue = UnionOfValues 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/bigint/cast.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Cast a string to a bigint literal type if possible. 3 | * 4 | * ```ts 5 | * StringToBigint<'1n'> // 1n 6 | * StringToBigint<'-1n'> // -1n 7 | * ``` 8 | */ 9 | export type StringToBigint = S extends `-0n` 10 | ? 0n 11 | : S extends `${infer N extends bigint}n` 12 | ? N 13 | : Fail 14 | -------------------------------------------------------------------------------- /packages/type-plus/src/binary/_binary.ts: -------------------------------------------------------------------------------- 1 | export * as B from './bit.js' 2 | export * as Bit from './bit.js' 3 | -------------------------------------------------------------------------------- /packages/type-plus/src/binary/bit.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | import type { Bit } from '../index.js' 3 | import { testType } from '../index.js' 4 | 5 | test('Bit.BitNot', () => { 6 | testType.equal, 0>(true) 7 | testType.equal, 1>(true) 8 | }) 9 | 10 | test('Bit.BitAnd', () => { 11 | testType.equal, 1>(true) 12 | testType.equal, 0>(true) 13 | testType.equal, 0>(true) 14 | testType.equal, 0>(true) 15 | }) 16 | 17 | test('Bit.BitOr', () => { 18 | testType.equal, 1>(true) 19 | testType.equal, 1>(true) 20 | testType.equal, 1>(true) 21 | testType.equal, 0>(true) 22 | }) 23 | 24 | test('Bit.BitXor', () => { 25 | testType.equal, 0>(true) 26 | testType.equal, 1>(true) 27 | testType.equal, 1>(true) 28 | testType.equal, 0>(true) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/type-plus/src/binary/bit.ts: -------------------------------------------------------------------------------- 1 | export type Bit = 0 | 1 2 | 3 | /** 4 | * Bitwise NOT operation. 5 | * 6 | * @since 🏷️ 8.0.0 7 | */ 8 | export type Not = X extends 0 ? 1 : 0 9 | 10 | /** 11 | * Bitwise AND operation. 12 | * 13 | * @since 🏷️ 8.0.0 14 | */ 15 | export type And = A extends 1 ? (B extends 1 ? 1 : 0) : 0 16 | 17 | /** 18 | * Bitwise OR operation. 19 | * 20 | * @since 🏷️ 8.0.0 21 | */ 22 | export type Or = A extends 1 ? 1 : B extends 1 ? 1 : 0 23 | 24 | /** 25 | * Bitwise XOR operation. 26 | * 27 | * @since 🏷️ 8.0.0 28 | */ 29 | export type Xor = A extends 1 ? Not : B 30 | -------------------------------------------------------------------------------- /packages/type-plus/src/binary/readme.md: -------------------------------------------------------------------------------- 1 | # Binary Types 2 | 3 | Binary types are types that act on binary data. 4 | 5 | Currently, we support single bit operations. 6 | They are grouped under the `Bit` (or alias `B`) namespace. 7 | 8 | - [`Bit.And`](./bit.ts): Bitwise `And` operator. 9 | - [`Bit.Or`](./bit.ts): Bitwise `Or` operator. 10 | - [`Bit.Not`](./bit.ts): Bitwise `Not` operator. 11 | - [`Bit.Xor`](./bit.ts): Bitwise `Xor` operator. 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/class/AnyConstructor.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import type { AnyConstructor } from '../index.js' 4 | 5 | test('basic', () => { 6 | // biome-ignore lint/complexity/useArrowFunction: on purpose 7 | const a = function () {} as any as AnyConstructor 8 | 9 | new a() 10 | }) 11 | 12 | test('specify params with tuple', () => { 13 | // biome-ignore lint/complexity/useArrowFunction: on purpose 14 | const a = function () {} as any as AnyConstructor<[count: number, value: string]> 15 | 16 | new a(1, 'a') 17 | }) 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/class/AnyConstructor.ts: -------------------------------------------------------------------------------- 1 | export type AnyConstructor = new (..._args: Params) => void 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/class/index.ts: -------------------------------------------------------------------------------- 1 | export type { AnyConstructor } from './AnyConstructor.js' 2 | export * from './isConstructor.js' 3 | -------------------------------------------------------------------------------- /packages/type-plus/src/class/isConstructor.ts: -------------------------------------------------------------------------------- 1 | import type { AnyConstructor } from './AnyConstructor.js' 2 | 3 | /** 4 | * Is the subject a constructor function. 5 | * 6 | * @deprecated this is not a failsafe test, 7 | * it will return true for any function that can be called with `new`. 8 | * 9 | * If the subject is an arrow function, 10 | * it can still return true after compilation. 11 | * 12 | * Thus this function is not safe to use. 13 | */ 14 | export function isConstructor(subject: unknown): subject is AnyConstructor { 15 | try { 16 | ;new (subject as AnyConstructor)() 17 | } catch (err: any) { 18 | const msg = err?.message as string | undefined 19 | if (msg && msg.indexOf('is not a constructor') >= 0) { 20 | return false 21 | } 22 | } 23 | return true 24 | } 25 | 26 | /** 27 | * instanceof type guard for unknown value. 28 | */ 29 | export function isInstanceof( 30 | subject: unknown, 31 | classConstructor: T, 32 | ): subject is InstanceType { 33 | return subject instanceof classConstructor 34 | } 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/composable_types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Types that can contain custom properties. 3 | */ 4 | export type ComposableTypes = object | Function 5 | 6 | /** 7 | * Types that cannot contain custom properties. 8 | */ 9 | export type NonComposableTypes = boolean | number | string | symbol | bigint | undefined | null 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/equal/identity_equal.ts: -------------------------------------------------------------------------------- 1 | import type { Equal } from './equal.js' 2 | 3 | /** 4 | * This is a common equal check. 5 | * It is good for some basic cases, but not for all. 6 | */ 7 | export type IdentityEqual = Equal.$Same 8 | -------------------------------------------------------------------------------- /packages/type-plus/src/function/any_function.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type AnyFunction, assertType } from '../index.js' 4 | 5 | test('basic', () => { 6 | function doCallback(cb: AnyFunction) { 7 | cb() 8 | } 9 | 10 | doCallback(() => {}) 11 | doCallback((_) => {}) 12 | doCallback((a: number, b: number) => a + b) 13 | }) 14 | 15 | test('define param as tuple', () => { 16 | const foo: AnyFunction<[number, string]> = (x) => x 17 | foo(1, 'a') 18 | }) 19 | 20 | test('define result type', () => { 21 | const foo: AnyFunction = (x) => x 22 | assertType.isString(foo('a')) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/function/any_function.ts: -------------------------------------------------------------------------------- 1 | export type AnyFunction = (...args: Params) => Result 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/function/extract_function.ts: -------------------------------------------------------------------------------- 1 | import type { AnyFunction } from './any_function.js' 2 | 3 | /** 4 | * Extract the function signature from a composite type T. 5 | * 6 | * It works with interact of functions, but not on function overloads and union. 7 | * @note does not work with function overloads. 8 | * 9 | * ```ts 10 | * import type { ExtractFunction } from 'type-plus' 11 | * 12 | * type R = ExtractFunction<{ 13 | * () => void 14 | * a: 1 15 | * }> // () => void 16 | * ``` 17 | */ 18 | export type ExtractFunction = T extends AnyFunction ? (...args: P) => R : never 19 | 20 | /** 21 | * Extract the function signature from a composite function. 22 | * 23 | * @note does not work with function overloads. 24 | */ 25 | export function extractFunction(fn: T) { 26 | return fn as unknown as ExtractFunction 27 | } 28 | -------------------------------------------------------------------------------- /packages/type-plus/src/functional/ChainFn.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type ChainFn, testType } from '../index.js' 4 | 5 | test('return type is the same as input type', () => { 6 | type A = ChainFn 7 | 8 | testType.equal[0], ReturnType>(true) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/functional/ChainFn.ts: -------------------------------------------------------------------------------- 1 | export type ChainFn = (param: T) => T 2 | 3 | /** 4 | * An endofunctor is a functor from one category back to the same category. 5 | */ 6 | export type EndoFn = (param: T) => T 7 | -------------------------------------------------------------------------------- /packages/type-plus/src/functional/Maybe.ts: -------------------------------------------------------------------------------- 1 | import type { IsEqual } from '../equal/is_equal.js' 2 | import type { Brand } from '../nominal/index.js' 3 | import type { Widen } from '../utils/index.js' 4 | 5 | export type Maybe = Just | None 6 | export type Just = Brand<'maybe', T> & { unwrap(): T } 7 | export type None = Brand<'maybe', void> & { unwrap(): T } 8 | 9 | export function just(value: T): IsEqual extends true ? None : Just>> { 10 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 11 | return { 12 | unwrap() { 13 | return value 14 | }, 15 | } as any 16 | } 17 | 18 | export function none(): None { 19 | return { unwrap() {} } as None 20 | } 21 | -------------------------------------------------------------------------------- /packages/type-plus/src/functional/compose.ts: -------------------------------------------------------------------------------- 1 | import type { Head } from '../array/head.js' 2 | import type { Last } from '../array/last.js' 3 | import type { AnyFunction } from '../function/any_function.js' 4 | 5 | /** 6 | * Compose functions to produce a new function. 7 | * @params args functions to be composed. 8 | * Each function will receive the return value of the previous function as its parameters. 9 | * @return The composed function will expect the parameters of the first function, 10 | * and return the result of the last function. 11 | */ 12 | export function compose(...fns: FS): (...args: Parameters>) => ReturnType> { 13 | return (...args: any[]) => fns.reduce((args, fn) => [fn.apply(null, args)], args)[0] 14 | } 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/functional/index.ts: -------------------------------------------------------------------------------- 1 | // export * from './Maybe.js' 2 | export type { ChainFn, EndoFn } from './ChainFn.js' 3 | export * from './compose.js' 4 | export * from './context.js' 5 | -------------------------------------------------------------------------------- /packages/type-plus/src/json.ts: -------------------------------------------------------------------------------- 1 | export type JSONTypes = JSONPrimitive | JSONObject | JSONArray 2 | 3 | export type JSONPrimitive = boolean | number | string | null 4 | 5 | export type JSONObject = { [key in string]?: JSONTypes } 6 | 7 | export type JSONArray = Array 8 | 9 | export const JSONTypes = { get } 10 | 11 | function get(obj: JSONTypes, ...props: Array): T | undefined { 12 | if (props.length === 0) return obj as T 13 | if (typeof obj !== 'object' || obj === null) return undefined 14 | const p = props.shift()! 15 | // @ts-ignore 16 | return get(obj[p], ...props) as T 17 | } 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/logical/readme.md: -------------------------------------------------------------------------------- 1 | # Logical Types 2 | 3 | Logical types are type-level logic operators. 4 | 5 | ## [`And`](./and.ts) 6 | 7 | 🎭 **predicate** 8 | 🏷️ **since 8.0.0** 9 | 10 | Logical `And` operator. 11 | 12 | ```ts 13 | type R = And // true 14 | type R = And // false 15 | ``` 16 | 17 | ## [`Or`](./or.ts) 18 | 19 | 🎭 **predicate** 20 | 🏷️ **since 8.0.0** 21 | 22 | Logical `Or` operator. 23 | 24 | ```ts 25 | type R = Or // true 26 | type R = Or // true 27 | type R = Or // false 28 | ``` 29 | 30 | ## [`Not`](./not.ts) 31 | 32 | 🎭 **predicate** 33 | 🏷️ **since 8.0.0** 34 | 35 | Logical `Not` operator. 36 | 37 | ```ts 38 | type R = Not // false 39 | type R = Not // true 40 | ``` 41 | 42 | ## [`Xor`](./xor.ts) 43 | 44 | 🎭 **predicate** 45 | 🏷️ **since 8.0.0** 46 | 47 | Logical `Xor` operator. 48 | 49 | ```ts 50 | type R = Xor // false 51 | type R = Xor // true 52 | type R = Xor // false 53 | ``` 54 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/abs.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type Abs, testType } from '../index.js' 4 | 5 | it('returns N if N is positive number', () => { 6 | testType.equal, 1>(true) 7 | testType.equal, 12345678901234>(true) 8 | }) 9 | 10 | it('returns N if N is positive bigint', () => { 11 | testType.equal, 1n>(true) 12 | testType.equal, 12345678901234n>(true) 13 | }) 14 | 15 | it('returns abs N if N is negative number', () => { 16 | testType.equal, 1234>(true) 17 | }) 18 | 19 | it('returns abs N if N is negative bigint', () => { 20 | testType.equal, 1234n>(true) 21 | }) 22 | 23 | it('number returns Fail', () => { 24 | testType.equal, never>(true) 25 | }) 26 | 27 | it('returns Fail if N is bigint type', () => { 28 | testType.equal, never>(true) 29 | }) 30 | 31 | it('can override Fail case', () => { 32 | testType.equal, 0>(true) 33 | testType.equal, 'ha'>(true) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/abs.ts: -------------------------------------------------------------------------------- 1 | import type { $Else, $Then } from '../$type/branch/$selection.js' 2 | import type { IsBigint } from '../bigint/is_bigint.js' 3 | import type { IsNumber } from '../number/is_number.js' 4 | 5 | /* 6 | * Returns the absolute value of a number or bigint `N`. 7 | * 8 | * @example 9 | * ```ts 10 | * Abs<-5> // 5 11 | * Abs<5> // 5 12 | * Abs<-1n> // 1n // Where `1n` is a bigint 13 | * ``` 14 | */ 15 | export type Abs = IsNumber extends infer R 16 | ? R extends $Then 17 | ? [number] extends [N] 18 | ? Fail 19 | : `${N}` extends `-${infer P extends number}` 20 | ? P 21 | : N 22 | : R extends $Else 23 | ? IsBigint extends infer R 24 | ? R extends true 25 | ? [bigint] extends [N] 26 | ? Fail 27 | : `${N}` extends `-${infer P extends bigint}` 28 | ? P 29 | : N 30 | : Fail 31 | : never 32 | : never 33 | : never 34 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/add.ts: -------------------------------------------------------------------------------- 1 | import type { NumericStruct } from './numeric_struct.js' 2 | 3 | export type Add = [ 4 | NumericStruct.FromNumeric, 5 | NumericStruct.FromNumeric, 6 | ] extends [infer MA, infer MB] 7 | ? MA extends NumericStruct 8 | ? MB extends NumericStruct 9 | ? NumericStruct.ToNumeric> 10 | : Fail 11 | : Fail 12 | : never 13 | 14 | export type Increment = Add 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/greater_than.ts: -------------------------------------------------------------------------------- 1 | import type { IsPositive } from '../numeric/is_positive.js' 2 | import type { Subtract } from './subtract.js' 3 | 4 | export type GreaterThan = Subtract< 5 | A, 6 | B, 7 | 'fail' 8 | > extends infer R extends number 9 | ? R extends 0 10 | ? false 11 | : IsPositive 12 | : Fail 13 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/index.ts: -------------------------------------------------------------------------------- 1 | export type { Abs } from './abs.js' 2 | // export type { Digit, DigitArray } from './Digit.js' 3 | export type { Add, Increment } from './add.js' 4 | export type { GreaterThan } from './greater_than.js' 5 | export type { Max } from './max.js' 6 | export type { Multiply } from './multiply.js' 7 | export type { Decrement, Subtract } from './subtract.js' 8 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/math_plus.to_negative.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type MathPlus, testType } from '../index.js' 4 | 5 | it('converts positive bigint to negative', () => { 6 | testType.equal, -1n>(true) 7 | }) 8 | 9 | it('converts positive number to negative', () => { 10 | testType.equal, -1>(true) 11 | }) 12 | 13 | it('converts 0 to 0', () => { 14 | testType.equal, 0>(true) 15 | testType.equal, 0n>(true) 16 | 17 | testType.equal, 0>(true) 18 | testType.equal, 0n>(true) 19 | }) 20 | 21 | it('returns N if N is negative', () => { 22 | testType.equal, -1>(true) 23 | testType.equal, -1n>(true) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/math_plus.to_negative.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a number or bigint `N` to negative. 3 | * If `N` is already negative, it returns itself. 4 | * 5 | * @example 6 | * ```ts 7 | * ToNegative<5> // -5 8 | * ToNegative<0> // 0 9 | * ToNegative<-5> // -5 10 | * ``` 11 | */ 12 | export type ToNegative = N extends number 13 | ? N extends 0 14 | ? 0 15 | : `-${N}` extends `${infer W extends number}` 16 | ? W 17 | : N 18 | : N extends 0n 19 | ? 0n 20 | : `-${N}` extends `${infer W extends bigint}` 21 | ? W 22 | : N 23 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/math_plus.ts: -------------------------------------------------------------------------------- 1 | export type { Add, Increment } from './add.js' 2 | export type { ToNegative } from './math_plus.to_negative.js' 3 | export type { Multiply } from './multiply.js' 4 | export type { Decrement, Subtract } from './subtract.js' 5 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/max.ts: -------------------------------------------------------------------------------- 1 | import type { IsNever } from '../never/is_never.js' 2 | import type { GreaterThan } from './greater_than.js' 3 | 4 | export type Max = GreaterThan< 5 | A, 6 | B 7 | > extends infer Result 8 | ? IsNever extends true 9 | ? Fail 10 | : Result extends true 11 | ? A 12 | : B 13 | : never 14 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/multiply.ts: -------------------------------------------------------------------------------- 1 | import type { NumericStruct } from './numeric_struct.js' 2 | 3 | export type Multiply = [ 4 | NumericStruct.FromNumeric, 5 | NumericStruct.FromNumeric, 6 | ] extends [infer MA, infer MB] 7 | ? MA extends NumericStruct 8 | ? MB extends NumericStruct 9 | ? NumericStruct.ToNumeric> 10 | : Fail 11 | : Fail 12 | : never 13 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/numeric_struct.digit_array.unit.ts: -------------------------------------------------------------------------------- 1 | import { describe, it } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | import type { DigitArray } from './numeric_struct.js' 5 | 6 | describe('Subtract', () => { 7 | it('A < B', () => { 8 | testType.equal, [-1, 1]>(true) 9 | 10 | testType.equal, [-8, -4, 0, 4, 8]>(true) 11 | 12 | testType.equal, [-2]>(true) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/math/subtract.ts: -------------------------------------------------------------------------------- 1 | import type { NumericStruct } from './numeric_struct.js' 2 | 3 | export type Subtract = [ 4 | NumericStruct.FromNumeric, 5 | NumericStruct.FromNumeric, 6 | ] extends [infer MA, infer MB] 7 | ? MA extends NumericStruct 8 | ? MB extends NumericStruct 9 | ? NumericStruct.ToNumeric> 10 | : Fail 11 | : Fail 12 | : never 13 | 14 | export type Decrement = Subtract 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/mix_types/box.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type Box, testType } from '../index.js' 4 | 5 | it('boxes arrow function to Function', () => { 6 | testType.equal void>, Function>(true) 7 | }) 8 | 9 | it('boxes literal to its boxed type', () => { 10 | testType.equal, Boolean>(true) 11 | testType.equal, Number>(true) 12 | testType.equal, String>(true) 13 | }) 14 | 15 | it('boxes object type', () => { 16 | testType.equal, Object>(true) 17 | }) 18 | 19 | it('keeps object literals', () => { 20 | testType.equal, { a: 1 }>(true) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/type-plus/src/mix_types/exclude.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type Exclude, testType } from '../index.js' 4 | 5 | it('returns T for special types', () => { 6 | testType.equal, any>(true) 7 | testType.equal, unknown>(true) 8 | testType.equal, never>(true) 9 | testType.equal, void>(true) 10 | }) 11 | 12 | it('exclude U from union', () => { 13 | testType.equal, 1>(true) 14 | }) 15 | 16 | it('defaults to replace U with never', () => { 17 | testType.equal, never>(true) 18 | }) 19 | 20 | it('replace U with R', () => { 21 | testType.equal, 1>(true) 22 | testType.equal, 2>(true) 23 | testType.equal, 1 | 2>(true) 24 | 25 | testType.equal, 'b' | 'c' | 'd'>(true) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/type-plus/src/mix_types/exclude.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 🌪️ *filter* 3 | * 4 | * Exclude from `T` those types that are assignable to `U`, 5 | * and replace them with `R`. 6 | * 7 | * This can be used as a drop-in replacement of the build-in `Exclude`. 8 | * 9 | * @example 10 | * ```ts 11 | * type R = Exclude // never 12 | * type R = Exclude // 1 13 | * 14 | * type R = Exclude // 2 15 | * type R = Exclude // 1 | 2 16 | * ``` 17 | */ 18 | export type Exclude = T extends U ? R : T 19 | -------------------------------------------------------------------------------- /packages/type-plus/src/mix_types/is_any_or_never.ts: -------------------------------------------------------------------------------- 1 | import type { $Selection } from '../$type/branch/$selection.js' 2 | import type { IsAny } from '../any/is_any.js' 3 | import type { IsNever } from '../never/is_never.js' 4 | 5 | /** 6 | * 🎭 *predicate* 7 | * 🔢 *customize* 8 | * 🩳 *shortcut* 9 | * 10 | * Validate if `T` is either exactly `any` or exactly `never`. 11 | * 12 | * @example 13 | * ```ts 14 | * type R = IsAnyOrNever // $Then 15 | * type R = IsAnyOrNever // $Then 16 | * 17 | * type R = IsAnyOrNever<1> // $Else 18 | * type R = IsAnyOrNever // $Else 19 | * 20 | * type R = IsAnyOrNever // true 21 | * type R = IsAnyOrNever<'a', $SelectionPredicate> // false 22 | * ``` 23 | */ 24 | export type IsAnyOrNever = IsNever< 25 | T, 26 | { 27 | $then: $O['$then'] 28 | $else: IsAny 29 | } 30 | > 31 | -------------------------------------------------------------------------------- /packages/type-plus/src/nodejs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './isNodeError.js' 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/nodejs/isNodeError.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, test } from '@jest/globals' 2 | 3 | import { assertType, isSystemError } from '../index.js' 4 | 5 | describe('isSystemError()', () => { 6 | test('ENOENT', () => { 7 | const s: unknown = {} 8 | if (isSystemError('ENOENT', s)) { 9 | assertType<'ENOENT'>(s.code) 10 | assertType(s.path) 11 | } 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/type-plus/src/nodejs/isNodeError.ts: -------------------------------------------------------------------------------- 1 | export type SystemErrors = { 2 | EACCES: Error & { code: 'EACCES' } 3 | EADDRINUSE: Error & { code: 'EADDRINUSE' } 4 | ECONNREFUSED: Error 5 | ECONNRESET: Error 6 | EEXIST: Error 7 | EISDIR: Error 8 | EMFILE: Error 9 | ENOENT: Error & { code: 'ENOENT'; path: string } 10 | ENOTDIR: Error 11 | ENOTEMPTY: Error 12 | ENOTFOUND: Error 13 | EPERM: Error 14 | EPIPE: Error 15 | ETIMEDOUT: Error 16 | } 17 | 18 | export type SystemErrorCodes = keyof SystemErrors 19 | 20 | /** 21 | * Type guard NodeJS SystemErrors. 22 | * The list is not complete. Will add as needed. 23 | * Feel free to contribute. 24 | */ 25 | export function isSystemError(code: C, err: unknown): err is SystemErrors[C] { 26 | return !!err && (err as any).code === code 27 | } 28 | -------------------------------------------------------------------------------- /packages/type-plus/src/nominal/constants.ts: -------------------------------------------------------------------------------- 1 | export const typeSym = Symbol('type') 2 | export const valueSym = Symbol('value') 3 | -------------------------------------------------------------------------------- /packages/type-plus/src/nominal/index.ts: -------------------------------------------------------------------------------- 1 | export * from './brand.js' 2 | export * from './flavor.js' 3 | export * from './nominal_match.js' 4 | -------------------------------------------------------------------------------- /packages/type-plus/src/nominal/nominal_match.ts: -------------------------------------------------------------------------------- 1 | import type { Brand } from './brand.js' 2 | import { typeSym } from './constants.js' 3 | import type { Flavor } from './flavor.js' 4 | 5 | export function nominalMatch(a: Brand, b: Brand): boolean 6 | export function nominalMatch(a: Flavor, b: Flavor): boolean 7 | export function nominalMatch( 8 | a: Brand | Flavor, 9 | b: Brand | Flavor, 10 | ) { 11 | if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) return a[typeSym] === b[typeSym] 12 | 13 | return true 14 | } 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/number/cast.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Cast a string to a number literal type if possible. 3 | * 4 | * ```ts 5 | * StringToNumber<'1'> // 1 6 | * StringToNumber<'-1'> // -1 7 | * ``` 8 | */ 9 | export type StringToNumber = S extends `-0` 10 | ? 0 11 | : S extends `${infer W}.0` 12 | ? StringToNumber 13 | : S extends `${infer W}.${infer F}0` 14 | ? StringToNumber<`${W}.${F}`> 15 | : S extends `${infer N extends number}` 16 | ? N 17 | : Fail 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/number/number_array.sum.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | it.todo('to be fixed') 3 | // import { testType } from '../index.js' 4 | // import type { Sum } from './number_plus.js' 5 | 6 | // it('returns 0 for empty tuple', () => { 7 | // testType.equal, 0>(true) 8 | // }) 9 | 10 | // it('returns never if A is number[]', () => { 11 | // testType.never>(true) 12 | // }) 13 | 14 | // it('returns sum of values', () => { 15 | // testType.equal, 1>(true) 16 | // testType.equal, 3>(true) 17 | // testType.equal, 6>(true) 18 | // }) 19 | 20 | // it.skip('supports negative numbers as long as result is > 0', () => { 21 | // // type.equal, 2>(true) 22 | // }) 23 | 24 | // it('can override fail case', () => { 25 | // testType.equal, -1>(true) 26 | // }) 27 | -------------------------------------------------------------------------------- /packages/type-plus/src/number/number_array.ts: -------------------------------------------------------------------------------- 1 | // import type { Add, IsStrictNumber } from '../index.js' 2 | 3 | // export type Sum = IsStrictNumber< 4 | // A['length'], 5 | // Fail, 6 | // A['length'] extends 0 7 | // ? 0 8 | // : A['length'] extends 1 9 | // ? A[0] 10 | // : A['length'] extends 2 11 | // ? Add 12 | // : A extends [infer F extends number, infer S extends number, ...infer Tail extends number[]] 13 | // ? Sum<[Add, ...Tail]> 14 | // : never 15 | // > 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/number/number_plus.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type NumberPlus, isType, testType } from '../index.js' 4 | 5 | it('exports', () => { 6 | isType(-0) 7 | testType.false>(true) 8 | }) 9 | -------------------------------------------------------------------------------- /packages/type-plus/src/number/number_plus.ts: -------------------------------------------------------------------------------- 1 | export type * from '../numeric/is_integer.js' 2 | export type * from '../numeric/is_negative.js' 3 | export type * from '../numeric/is_not_integer.js' 4 | export type * from '../numeric/is_not_negative.js' 5 | export type * from '../numeric/is_not_numeric.js' 6 | export type * from '../numeric/is_not_positive.js' 7 | export type * from '../numeric/is_numeric.js' 8 | export type * from '../numeric/is_positive.js' 9 | export type * from '../numeric/numeric_type.js' 10 | export type * from './is_not_number.js' 11 | export type * from './is_number.js' 12 | 13 | // export type { Sum } from './number_array.js' 14 | -------------------------------------------------------------------------------- /packages/type-plus/src/numeric/cast.ts: -------------------------------------------------------------------------------- 1 | import type { StringToBigint } from '../bigint/cast.js' 2 | import type { StringToNumber } from '../number/cast.js' 3 | 4 | /** 5 | * Cast a string to a numeric literal type (number or bigint) if possible. 6 | * 7 | * ```ts 8 | * StringToNumeric<'1'> // 1 9 | * StringToNumeric<'1n'> // 1n 10 | * StringToNumeric<'-1'> // -1 11 | * StringToNumeric<'-1n'> // -1n 12 | * ``` 13 | */ 14 | export type StringToNumeric = StringToBigint> 15 | 16 | /** 17 | * Cast a numeric literal type (number or bigint) to string. 18 | * 19 | * ```ts 20 | * NumericToString<1> // '1' 21 | * NumericToString<1.23> // '1.23' 22 | * NumericToString<0.00123> // '0.00123' 23 | * NumericToString<1n> // '1n' 24 | * NumericToString<-1> // '-1' 25 | * NumericToString<-1n> // '-1n' 26 | * ``` 27 | */ 28 | export type NumericToString = N extends number ? `${N}` : `${N}n` 29 | -------------------------------------------------------------------------------- /packages/type-plus/src/numeric/is_not_numeric.ts: -------------------------------------------------------------------------------- 1 | import type { $SelectInvert } from '../equal/equal.js' 2 | 3 | /** 4 | * Is `T` not numeric. 5 | * 6 | * ```ts 7 | * type R = IsNotNumeric<1> // false 8 | * type R = IsNotNumeric<1.1> // false 9 | * 10 | * type R = IsNotNumeric // true 11 | * type R = IsNotNumeric // true 12 | * ``` 13 | */ 14 | 15 | export type IsNotNumeric = $SelectInvert 16 | 17 | export namespace IsNotNumeric { 18 | export type $Options = $SelectInvert.$Options 19 | export type $Default = $SelectInvert.$Default 20 | export type $Branch = $SelectInvert.$Branch 21 | } 22 | -------------------------------------------------------------------------------- /packages/type-plus/src/numeric/numeric.zero.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type Zero, isType } from '../index.js' 4 | 5 | it('can be 0', () => { 6 | isType(0) 7 | }) 8 | 9 | it('can be bigint 0n', () => { 10 | isType(0n) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/numeric/numeric_plus.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type NumericPlus, isType } from '../index.js' 4 | 5 | it('exports', () => { 6 | isType(-0) 7 | isType(1) 8 | }) 9 | -------------------------------------------------------------------------------- /packages/type-plus/src/numeric/numeric_plus.ts: -------------------------------------------------------------------------------- 1 | export type * from './is_integer.js' 2 | export type * from './is_negative.js' 3 | export type * from './is_not_integer.js' 4 | export type * from './is_not_negative.js' 5 | export type * from './is_not_numeric.js' 6 | export type * from './is_not_positive.js' 7 | export type * from './is_numeric.js' 8 | export type * from './is_positive.js' 9 | export type * from './numeric_type.js' 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/numeric/numeric_type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Either number or bigint. 3 | */ 4 | export type Numeric = number | bigint 5 | 6 | /** 7 | * The value 0 in number or bigint. 8 | */ 9 | export type Zero = 0 | 0n 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/ANotB.ts: -------------------------------------------------------------------------------- 1 | import type { IsEqual } from '../equal/is_equal.js' 2 | import type { IsDisjoint } from './IsDisjoint.js' 3 | import type { KeysWithDiffType } from './KeysWithDiffType.js' 4 | import type { AnyRecord } from './any_record.js' 5 | 6 | export type ANotB = IsEqual extends true 7 | ? never 8 | : IsDisjoint extends true 9 | ? A 10 | : { [k in Exclude | KeysWithDiffType]: A[k] } 11 | 12 | export type BNotA = ANotB 13 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/ExcludePropType.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type ExcludePropType, assertType } from '../index.js' 4 | 5 | test('exclude type R from properties of T', () => { 6 | interface Customer { 7 | name: string 8 | age: number | null 9 | } 10 | 11 | type CustomerAgeNotNull = ExcludePropType 12 | 13 | const x: CustomerAgeNotNull = { name: '', age: 0 } 14 | assertType.isNumber(x.age) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/ExcludePropType.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Exclude type U from properties in T. 3 | */ 4 | export type ExcludePropType, U> = { 5 | [k in keyof T]: Exclude 6 | } 7 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/IsDisjoint.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type IsDisjoint, testType } from '../index.js' 4 | 5 | test('disjoint returns true', () => { 6 | type A = { a: 1 } 7 | type B = { b: 1 } 8 | testType.true>(true) 9 | }) 10 | 11 | test('same type returns false', () => { 12 | type A = { a: 1 } 13 | type B = { a: 1 } 14 | testType.false>(true) 15 | }) 16 | 17 | test('A subset of B returns false', () => { 18 | type A = { a: 1 } 19 | type B = { a: 1; b: 1 } 20 | testType.false>(true) 21 | }) 22 | 23 | test('B subset of A returns false', () => { 24 | type A = { a: 1; b: 1 } 25 | type B = { a: 1 } 26 | testType.false>(true) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/IsDisjoint.ts: -------------------------------------------------------------------------------- 1 | import type { And, Not } from '../predicates/index.js' 2 | import type { AnyRecord } from './any_record.js' 3 | import type { HasKey } from './hasKey.js' 4 | 5 | /** 6 | * Are the two records disjoint from each other. 7 | * Disjoint means no common property. 8 | */ 9 | export type IsDisjoint = And>, Not>> 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/IsRecord.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type IsRecord, assertType } from '../index.js' 4 | 5 | test('boolean, number, string, null, undefined, symbol are not record', () => { 6 | assertType.isFalse(false as IsRecord) 7 | assertType.isFalse(false as IsRecord) 8 | assertType.isFalse(false as IsRecord) 9 | assertType.isFalse(false as IsRecord) 10 | assertType.isFalse(false as IsRecord) 11 | assertType.isFalse(false as IsRecord) 12 | }) 13 | 14 | test('array is not record', () => { 15 | assertType.isFalse(false as IsRecord<[]>) 16 | }) 17 | 18 | test('object is record', () => { 19 | // eslint-disable-next-line @typescript-eslint/ban-types 20 | assertType.isTrue(true as IsRecord<{}>) 21 | // eslint-disable-next-line @typescript-eslint/ban-types 22 | assertType.isTrue(true as IsRecord) 23 | assertType.isTrue(true as IsRecord<{ a: string }>) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/IsRecord.ts: -------------------------------------------------------------------------------- 1 | export type IsRecord = T extends any[] ? false : T extends Record ? true : false 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/KeyTypes.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import type { KeyTypes } from '../index.js' 4 | 5 | it('contains type of all keys', () => { 6 | acceptKeys('a') 7 | acceptKeys(1) 8 | acceptKeys(Symbol()) 9 | 10 | function acceptKeys(k: KeyTypes) { 11 | return k 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/KeyTypes.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Type of all keys. 3 | * To get the keys of an object or array, use the `keyof` keyword. 4 | * This is just a convenient type as `keyof any` is not obvious. 5 | */ 6 | export type KeyTypes = keyof any 7 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/KeyofOptional.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type KeysOfOptional, testType } from '../index.js' 4 | 5 | it('get keys from optional type', () => { 6 | type X = { o?: { a: string; b: string } } 7 | type A = KeysOfOptional 8 | 9 | testType.equal(true) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/KeyofOptional.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @deprecated renamed to `KeysOfOptional` 3 | */ 4 | export type KeyofOptional = T extends Record ? U : never 5 | 6 | export type KeysOfOptional = T extends Record ? U : never 7 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/KeysWithDiffType.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type KeysWithDiffType, assertType } from '../index.js' 4 | 5 | test('disjoint type gets never', () => { 6 | type A = { a: 1 } 7 | type B = { b: 2 } 8 | const actual = 'a' as KeysWithDiffType 9 | assertType(actual) 10 | }) 11 | 12 | test('key with same type is not included', () => { 13 | type A = { a: 1 } 14 | const actual = 'a' as KeysWithDiffType 15 | assertType(actual) 16 | }) 17 | 18 | test('key with different type is returned', () => { 19 | type A = { a: 1 } 20 | type B = { a: 2 } 21 | const actual = 'a' as KeysWithDiffType 22 | assertType<'a'>(actual) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/KeysWithDiffType.ts: -------------------------------------------------------------------------------- 1 | import type { IsDisjoint } from './IsDisjoint.js' 2 | import type { ValueOf } from './ValueOf.js' 3 | import type { AnyRecord } from './any_record.js' 4 | 5 | export type KeysWithDiffType = IsDisjoint extends true 6 | ? never 7 | : ValueOf<{ 8 | [k in keyof A & keyof B]: A[k] extends B[k] ? never : k 9 | }> 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/KnownKeys.ts: -------------------------------------------------------------------------------- 1 | // Original by Klaus Meinhardt @ajafff 2 | // known from Gerrit Birkeland @Gerrit0 3 | // https://github.com/Microsoft/TypeScript/issues/25987#issuecomment-408339599 4 | 5 | import type { PrimitiveTypes } from '../primitive.js' 6 | 7 | // https://github.com/microsoft/TypeScript/issues/25987#issuecomment-441224690 8 | export type KnownKeys = T extends PrimitiveTypes 9 | ? never 10 | : { 11 | [K in keyof T]: string extends K ? never : number extends K ? never : K 12 | } extends { [_ in keyof T]: infer U } 13 | ? // eslint-disable-next-line @typescript-eslint/ban-types 14 | {} extends U 15 | ? never 16 | : U 17 | : never 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/OptionalKeys.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | import type { OptionalKeys } from './optional_key.js' 5 | 6 | test('extract optional keys from object', () => { 7 | type X = { a?: string; b: string } 8 | 9 | testType.equal<'a', OptionalKeys>(true) 10 | }) 11 | test('work with union type', () => { 12 | type X = { a?: string; b: string } | { c: string; d?: string } 13 | 14 | type A = OptionalKeys 15 | testType.equal<'a' | 'd', A>(true) 16 | }) 17 | test('no optional keys returns never', () => { 18 | type X = { b: string } 19 | 20 | testType.never>(true) 21 | }) 22 | test('keys with undefined value is not optional', () => { 23 | type X = { a: string | undefined } 24 | 25 | testType.never>(true) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/Partial.ts: -------------------------------------------------------------------------------- 1 | import type { UnionKeys } from '../union_keys.js' 2 | import type { Omit } from './omit.js' 3 | import type { Pick } from './pick.js' 4 | 5 | /** 6 | * An alternative `Partial` type that works with `exactOptionalPropertyTypes` 7 | */ 8 | export type Partial = { [P in keyof T]?: T[P] | undefined } 9 | 10 | /** 11 | * Apply `Partial<>` on the selected properties. 12 | */ 13 | export type PartialPick> = T extends T ? Omit & Partial> : never 14 | 15 | /** 16 | * @deprecated replaced by `PartialOmit` 17 | */ 18 | export type PartialExcept> = T extends T ? Pick & Partial> : never 19 | 20 | /** 21 | * Apply `Partial<>` on all not selected properties. 22 | */ 23 | export type PartialOmit> = T extends T ? Pick & Partial> : never 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/RecursiveIntersect.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import type { RecursiveIntersect } from '../index.js' 4 | 5 | test('add object type to types', () => { 6 | type U = { u: number } 7 | 8 | acceptU(addU('a')) 9 | acceptU(addU(1)) 10 | acceptU(addU(true)) 11 | // acceptU(addU(1n)) 12 | acceptU(addU(undefined)) 13 | acceptU(addU(null)) 14 | 15 | const obj = addU({ x: 1 }) 16 | acceptU(obj) 17 | acceptU(obj.x) 18 | 19 | const comObj = addU({ array: ['a'] }) 20 | acceptU(comObj) 21 | acceptU(comObj.array) 22 | acceptU(comObj.array[0]!) 23 | 24 | const arr = addU(['1']) 25 | acceptU(arr) 26 | acceptU(arr[0]!) 27 | 28 | // Not supported 29 | // const comArr = addU([{ x: 1 }]) 30 | // acceptU(comArr) 31 | // acceptU(comArr[0]) 32 | // acceptU(comArr[0].x) 33 | 34 | function addU(x: T): RecursiveIntersect { 35 | return x as any as RecursiveIntersect 36 | } 37 | 38 | function acceptU(x: U) { 39 | return x 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/RecursiveIntersect.ts: -------------------------------------------------------------------------------- 1 | import type { AnyRecord } from './any_record.js' 2 | 3 | /** 4 | * Intersect type recursively. 5 | * The recursion terminates at level 7 due to design limit of TypeScript. 6 | * 7 | * Normal use case is intersecting betwee two object types. 8 | * While it works for value types and top level array, 9 | * top level array does not recursive into the elements. 10 | * NOTE: in latest TypeScript, 11 | * `undefined` is not an accepted value. 12 | * The resulting type would be `never` 13 | */ 14 | export type RecursiveIntersect = T & 15 | (T extends Array 16 | ? Array & U 17 | : T extends AnyRecord 18 | ? { 19 | [P in keyof T]: T[P] extends Array 20 | ? Array> & U 21 | : T[P] extends AnyRecord 22 | ? RecursiveIntersect 23 | : T[P] & U 24 | } & U 25 | : U) 26 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/RecursiveRequired.ts: -------------------------------------------------------------------------------- 1 | import type { AnyRecord } from './any_record.js' 2 | 3 | export type RecursiveRequired = { 4 | [P in keyof T]-?: T[P] extends (infer U)[] 5 | ? RecursiveRequired[] 6 | : T[P] extends AnyRecord 7 | ? RecursiveRequired 8 | : T[P] 9 | } 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/Required.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type RequiredExcept, type RequiredPick, assertType, testType } from '../index.js' 4 | 5 | test('make picked properties required', () => { 6 | type Foo = { 7 | a?: number | undefined 8 | b?: number | undefined 9 | c: number 10 | } 11 | 12 | const y: RequiredPick = { a: 1, c: 2 } 13 | 14 | testType.equal(true) 15 | y.b = undefined 16 | testType.equal(true) 17 | }) 18 | 19 | test('make not picked properties required', () => { 20 | type Foo = { 21 | a?: number | undefined 22 | b?: number | undefined 23 | c: number 24 | } 25 | 26 | const y: RequiredExcept = { b: 1, c: 2 } 27 | 28 | y.a = undefined 29 | assertType.noUndefined(y.b) 30 | assertType.noUndefined(y.c) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/Required.ts: -------------------------------------------------------------------------------- 1 | // Thanks [jack-williams](https://github.com/jack-williams) for the [solution](https://github.com/Microsoft/TypeScript/issues/29269#issuecomment-451602962) 2 | 3 | export type Required = { [P in keyof T]-?: Exclude } 4 | 5 | export type RequiredPick = Required> & Pick> 6 | 7 | export type RequiredExcept = Required>> & Pick 8 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/RequiredKeys.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type RequiredKeys, testType } from '../index.js' 4 | 5 | test('extract required keys from object', () => { 6 | type X = { a?: string; b: string } 7 | 8 | type A = RequiredKeys 9 | testType.equal(true) 10 | }) 11 | 12 | test('work with union type', () => { 13 | type X = { a?: string; b: string } | { c: string; d?: string } 14 | 15 | type A = RequiredKeys 16 | testType.equal(true) 17 | }) 18 | test('no required keys returns never', () => { 19 | type X = { b?: string } 20 | 21 | testType.never>(true) 22 | }) 23 | test('keys with undefined value is required', () => { 24 | type X = { a: string | undefined } 25 | 26 | testType.equal, 'a'>(true) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/RequiredKeys.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | import type { OptionalKeys } from './optional_key.js' 3 | 4 | export type RequiredKeys> = T extends unknown ? RequiredKeys._ : never 5 | 6 | export namespace RequiredKeys { 7 | export type _> = Exclude> 8 | } 9 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/SpreadRecord.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type SpreadRecord, testType } from '../index.js' 4 | 5 | test('records are combined as intersection', () => { 6 | type S = SpreadRecord<{ a: number }, { b: string }> 7 | testType.equal(true) 8 | }) 9 | 10 | test('Property in B overrides A', () => { 11 | type A = { a: number } 12 | type B = { a: string; b: string } 13 | type S = SpreadRecord 14 | 15 | testType.equal(true) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/SpreadRecord.ts: -------------------------------------------------------------------------------- 1 | import type { Omit } from '../object/index.js' 2 | 3 | export type SpreadRecord, B extends Record> = Omit> & 4 | B 5 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/ValueOf.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type ValueOf, isType } from '../index.js' 4 | 5 | test('work with primitive type', () => { 6 | type A = ValueOf 7 | isType.equal() 8 | }) 9 | 10 | test('If all values has the same type, the result is of that type', () => { 11 | const HTTP_METHOD = { 12 | GET: 'GET', 13 | POST: 'POST', 14 | DELETE: 'DELETE', 15 | PUT: 'PUT', 16 | } 17 | type A = ValueOf 18 | isType.equal() 19 | }) 20 | 21 | test('If value has multiple types, the result is the union of those types', () => { 22 | const logLevel = { 23 | none: '0', 24 | error: '1', 25 | warn: 2, 26 | info: 3, 27 | debug: 4, 28 | } 29 | type A = ValueOf 30 | isType.equal() 31 | }) 32 | 33 | test('literal types are preserved', () => { 34 | type L = { a: 1; b: 2; c: 'a'; d: 'b' } 35 | type A = ValueOf 36 | isType.equal() 37 | }) 38 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/ValueOf.ts: -------------------------------------------------------------------------------- 1 | export type ValueOf = T[keyof T] 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/adjust_exact_optional_props.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import type { AdjustExactOptionalProps } from '../index.js' 3 | import { testType } from '../testing/test_type.js' 4 | 5 | it('adds undefined to optional props', () => { 6 | type X = { a: string; b?: string } 7 | testType.equal, { a: string; b?: string | undefined }>(true) 8 | }) 9 | 10 | it('works with empty object', () => { 11 | type X = {} 12 | testType.equal, {}>(true) 13 | }) 14 | 15 | it('works with intersection', () => { 16 | type X = { a: string; b?: string } & { c: string } 17 | testType.equal, { a: string; b?: string | undefined; c: string }>(true) 18 | }) 19 | 20 | it('works with union', () => { 21 | type X = { a: string; b?: string } | { c: string } 22 | testType.equal, { a: string; b?: string | undefined } | { c: string }>(true) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/adjust_exact_optional_props.ts: -------------------------------------------------------------------------------- 1 | import type { RequiredKeys } from './RequiredKeys.js' 2 | import type { AnyRecord } from './any_record.js' 3 | import type { OptionalKeys } from './optional_key.js' 4 | 5 | /** 6 | * ⚗️ *transform* 7 | * 8 | * Adjust `T` to work with compiler flag [exactOptionalPropertyTypes](https://www.typescriptlang.org/tsconfig/#exactOptionalPropertyTypes). 9 | * 10 | * It adds `undefined` to optional properties. 11 | */ 12 | export type AdjustExactOptionalProps = T extends object 13 | ? { [K in OptionalKeys]?: T[K] | undefined } & { [K in RequiredKeys]: T[K] } 14 | : never 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/any_record.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export type AnyRecord = Record 4 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/everyKey.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { everyKey } from '../index.js' 4 | 5 | test('predicate key can be used as indexer of the subject', () => { 6 | const subject = { a: 1, b: 2, c: 3 } 7 | expect(everyKey(subject, (key) => typeof subject[key] === 'number')).toBe(true) 8 | }) 9 | 10 | test('type this arg', () => { 11 | const subject = { b: 2 } 12 | expect( 13 | everyKey( 14 | subject, 15 | function () { 16 | return this.a === 1 17 | }, 18 | { a: 1 }, 19 | ), 20 | ).toBe(true) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/everyKey.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export function everyKey, T = any>( 4 | subject: S, 5 | predicate: (this: T, key: keyof S, index: number, array: string[]) => unknown, 6 | thisArg?: T, 7 | ): boolean { 8 | return Object.keys(subject).every(predicate, thisArg) 9 | } 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/facade.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { facade } from '../index.js' 4 | 5 | test('facade on property', () => { 6 | const subject = { a: 1, b: 2, c: 3 } 7 | 8 | const actual = facade(subject, 'a', 'b') 9 | expect(actual.a).toBe(1) 10 | expect(actual.b).toBe(2) 11 | 12 | actual.b = 4 13 | expect(subject.b).toBe(4) 14 | }) 15 | 16 | test('facade on function', () => { 17 | const subject = { 18 | a: 1, 19 | foo() { 20 | return this.a 21 | }, 22 | } 23 | 24 | const actual = facade(subject, 'foo') 25 | expect(actual.foo()).toBe(1) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/filterKey.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from '@jest/globals' 2 | 3 | import { filterKey } from '../index.js' 4 | 5 | it(`returns with type as 'keyof subject'`, () => { 6 | const subject = { a: 1, b: 2, c: 3 } 7 | const actual = filterKey(subject, (key) => subject[key] > 1) 8 | expect(actual).toEqual(['b', 'c']) 9 | }) 10 | 11 | it('includes subject in callback', () => { 12 | const subject = { a: 1, b: 2, c: 3 } 13 | const actual = filterKey(subject, (key, _i, _a, s) => s[key] > 1) 14 | expect(actual).toEqual(['b', 'c']) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/filterKey.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export function filterKey, T = any>( 4 | subject: S, 5 | predicate: (this: T, key: keyof S, index: number, obj: Array, subject: S) => boolean, 6 | thisArg?: T, 7 | ): Array { 8 | return Object.keys(subject).filter(function (this: T, k, i, a) { 9 | return predicate.apply(this, [k, i, a, subject]) 10 | }, thisArg) 11 | } 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/findKey.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from '@jest/globals' 2 | 3 | import { findKey } from '../index.js' 4 | 5 | it('pass key as indexer of the subject', () => { 6 | const subject = { a: 1, b: 2, c: 3 } 7 | const actual = findKey(subject, (key) => subject[key] === 2) 8 | expect(actual).toEqual('b') 9 | }) 10 | 11 | it('includes subject in callback', () => { 12 | const subject = { a: 1, b: 2, c: 3 } 13 | const actual = findKey(subject, (key, _i, _a, s) => s[key] === 2) 14 | expect(actual).toEqual('b') 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/findKey.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export function findKey, T = any>( 4 | subject: S, 5 | predicate: (this: T, key: keyof S, index: number, obj: Array, subject: S) => boolean, 6 | thisArg?: T, 7 | ): keyof S | undefined { 8 | return Object.keys(subject).find(function (this: T, k, i, a) { 9 | return predicate.apply(this, [k, i, a, subject]) 10 | }, thisArg) 11 | } 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/forEachKey.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from '@jest/globals' 2 | 3 | import { forEachKey } from '../index.js' 4 | 5 | it('predicate key can be used as indexer of the subject', () => { 6 | const subject = { a: 1, b: 2, c: 3 } 7 | let actual = 0 8 | forEachKey(subject, (key) => (actual += subject[key])) 9 | expect(actual).toEqual(6) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/forEachKey.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export function forEachKey, T = any>( 4 | subject: S, 5 | predicate: (this: T, key: keyof S, index: number, obj: Array) => void, 6 | thisArg?: T, 7 | ): void { 8 | Object.keys(subject).forEach(predicate, thisArg) 9 | } 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/getField.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { getField } from '../index.js' 4 | 5 | test('support undefined', () => { 6 | const sub: { a: number } | undefined = { a: 1 } 7 | expect(getField(sub, 'a')).toBe(1) 8 | }) 9 | 10 | test('support null', () => { 11 | const sub: { a: number } | null = { a: 1 } 12 | expect(getField(sub, 'a')).toBe(1) 13 | }) 14 | 15 | test('can specify default value', () => { 16 | const sub: { a?: number | undefined } = { a: undefined } 17 | expect(getField(sub, 'a', 2)).toBe(2) 18 | }) 19 | 20 | test('get from union keys', () => { 21 | const sub: { a: number; b: string } | { a: number; c: string } = { a: 1, b: 'b' } 22 | expect(getField(sub, 'b')).toBe('b') 23 | }) 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/getField.ts: -------------------------------------------------------------------------------- 1 | import type { UnionKeys } from '../union_keys.js' 2 | 3 | export function getField, K extends UnionKeys>(subject: T, key: K): TX[K] 4 | export function getField< 5 | T, 6 | TX extends Exclude, 7 | K extends UnionKeys, 8 | DV extends Exclude, 9 | >(subject: T, key: K, defaultValue: DV): DV 10 | export function getField< 11 | T, 12 | TX extends Exclude, 13 | K extends UnionKeys, 14 | DV extends Exclude, 15 | >(subject: T, key: K, defaultValue?: DV) { 16 | return (subject && (subject as unknown as { [k in K]: TX[K] | DV })[key]) || defaultValue 17 | } 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/hasKey.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals' 2 | 3 | import { type HasKey, assertType, hasKey } from '../index.js' 4 | 5 | describe('HasKey', () => { 6 | test('true if has key', () => { 7 | type Foo = { a: 1; b: 2 } 8 | assertType.isTrue(true as HasKey) 9 | }) 10 | 11 | test('false if do not have key', () => { 12 | type Foo = { a: 1; b: 2 } 13 | assertType.isFalse(false as HasKey) 14 | }) 15 | }) 16 | 17 | describe('hasKey()', () => { 18 | test('true if has key', () => { 19 | const subject = { a: 1, b: 2 } 20 | 21 | expect(hasKey(subject, 'a')).toBeTruthy() 22 | expect(hasKey(subject, 'b')).toBeTruthy() 23 | expect(hasKey(subject, 'a', 'b')).toBeTruthy() 24 | }) 25 | test('false if do not have key', () => { 26 | const subject = { a: 1, b: 2 } 27 | 28 | expect(hasKey(subject, 'c')).toBeFalsy() 29 | expect(hasKey(subject, 'a', 'c')).toBeFalsy() 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/hasKey.ts: -------------------------------------------------------------------------------- 1 | import type { AnyRecord } from './any_record.js' 2 | 3 | export type HasKey = K extends keyof T ? Then : Else 4 | 5 | export function hasKey(subject: T, ...keys: K[]): HasKey { 6 | return !keys.some((key) => !subject[key]) as unknown as HasKey 7 | } 8 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/hasProperty.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { hasProperty } from '../index.js' 4 | 5 | test('hasProperty', () => { 6 | type X = { name: string } & ({ a: 1 } | { b: 2 }) 7 | 8 | const x: X = { name: 'n', a: 1 } 9 | 10 | if (hasProperty(x, 'a')) expect(x.a).toBe(1) 11 | 12 | const y: X = { name: 'n', b: 2 } 13 | 14 | if (hasProperty(y, 'b')) expect(y.b).toBe(2) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/hasProperty.ts: -------------------------------------------------------------------------------- 1 | import type { UnionKeys } from '../union_keys.js' 2 | 3 | export function hasProperty>(value: T, prop: P): value is T & Record { 4 | return !!value[prop] 5 | } 6 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/left_join.ts: -------------------------------------------------------------------------------- 1 | import type { IsEqual } from '../equal/is_equal.js' 2 | import type { IsDisjoint } from './IsDisjoint.js' 3 | import type { AnyRecord } from './any_record.js' 4 | import type { Properties } from './properties.js' 5 | 6 | export type LeftJoin = IsEqual extends true 7 | ? A 8 | : IsDisjoint extends true 9 | ? A & B 10 | : Properties<{ [k in Exclude]: A[k] } & { [k in keyof B]: B[k] }> 11 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/mapKey.spec.ts: -------------------------------------------------------------------------------- 1 | import { it, test } from '@jest/globals' 2 | import { a } from 'assertron' 3 | 4 | import { mapKey } from '../index.js' 5 | 6 | test('predicate key can be used as indexer of the subject', () => { 7 | const subject = { a: 1, b: 2, c: 3 } 8 | const actual = mapKey(subject, (key) => subject[key] + 1) 9 | a.satisfies(actual, [2, 3, 4]) 10 | }) 11 | 12 | it('includes subject in callback', () => { 13 | const subject = { a: 1, b: 2, c: 3 } 14 | const actual = mapKey(subject, (key, _i, _a, s) => s[key] + 1) 15 | a.satisfies(actual, [2, 3, 4]) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/mapKey.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export function mapKey, T = any>( 4 | subject: S, 5 | predicate: (this: T, key: keyof S, index: number, obj: Array, subject: S) => R, 6 | thisArg?: T, 7 | ): R[] { 8 | return Object.keys(subject).map(function (this: T, k, i, a) { 9 | return predicate.apply(this, [k, i, a, subject]) 10 | }, thisArg) 11 | } 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/mapProperties.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { mapProperties } from '../index.js' 4 | 5 | test('basic usage', () => { 6 | const actual = mapProperties({ a: 1, b: 2 }, (v, k) => k + String(v * 2)) 7 | expect(actual).toEqual({ a: 'a2', b: 'b4' }) 8 | }) 9 | 10 | test('mixed type', () => { 11 | const actual = mapProperties({ a: 1, b: 'b' }, (v, k) => k + String(v)) 12 | expect(actual).toEqual({ a: 'a1', b: 'bb' }) 13 | }) 14 | 15 | test('extract value from prop', () => { 16 | const actual = mapProperties({ a: { foo: 'x' }, b: { foo: 'y' } }, (v) => v.foo) 17 | expect(actual).toEqual({ a: 'x', b: 'y' }) 18 | }) 19 | 20 | test('parent props are not mapped', () => { 21 | class Boo { 22 | shared(v: number) { 23 | return v * 2 24 | } 25 | own = (v: number) => v * 3 26 | } 27 | const boo = new Boo() 28 | 29 | const actual = mapProperties(boo, (v) => v(2)) 30 | expect(actual).toEqual({ own: 6 }) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/mapProperties.ts: -------------------------------------------------------------------------------- 1 | import type { ValueOf } from './ValueOf.js' 2 | import type { AnyRecord } from './any_record.js' 3 | import { reduceByKey } from './reduceKey.js' 4 | 5 | /** 6 | * An Object-specific version of `map`. 7 | * Original source: 8 | * 9 | * 10 | * `ramda` has a similar function (`mapObjIndexed()`) with different parameter order. 11 | * I keep this parameter order because this parameter order provides better type inference. 12 | */ 13 | export function mapProperties( 14 | subject: Subject, 15 | callbackfn: (value: ValueOf, key: keyof Subject, obj: Subject) => ResultProp, 16 | ): { [K in keyof Subject]: ResultProp } { 17 | return reduceByKey( 18 | subject, 19 | (p, key) => ((p[key] = callbackfn(subject[key], key, subject)), p), 20 | {} as { [K in keyof Subject]: ResultProp }, 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/object_plus.ts: -------------------------------------------------------------------------------- 1 | export * from './merge.js' 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/omit.unit.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | import type { Omit } from '../index.js' 3 | 4 | it('intersection types with generic', () => { 5 | // note this is not a typical use case. 6 | // it is used to show that the assignability is still working. 7 | type Foo = { a: string; b: string } 8 | function foo(input: Omit): void { 9 | input.a = '1' 10 | // @ts-expect-error Property 'b' does not exist 11 | input.b = '1' 12 | 13 | // @ts-expect-error Property 'c' does not exist. 14 | input.c = '1' 15 | } 16 | foo({ a: '1' }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/properties.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets the properties of an object type. 3 | */ 4 | export type Properties = { [k in keyof T]: T[k] } // & {} 5 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/record.ts: -------------------------------------------------------------------------------- 1 | import type { Widen } from '../utils/index.js' 2 | import type { KeyTypes } from './KeyTypes.js' 3 | import type { AnyRecord } from './any_record.js' 4 | 5 | /** 6 | * Creates a `Record` or your custom record. 7 | * By default, 8 | * `record()` will widen the keys (`K`) you specified in the `value` to form `Record, V>`. 9 | * 10 | * You can also override it by specifying a custom record, e.g.: 11 | * `record<{ a: number }>()` 12 | */ 13 | export function record(value?: Record): Record, V> 14 | export function record>(value?: R): R 15 | export function record(value?: any) { 16 | const r = Object.create(null) as AnyRecord 17 | return (value ? Object.assign(r, value) : r) as AnyRecord 18 | } 19 | 20 | /** 21 | * Gets the value type `T` from `Record`. 22 | */ 23 | export type RecordValue> = R extends Record ? T : never 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/reduceKey.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export function reduceByKey, T>( 4 | subject: S, 5 | callbackfn: (previousValue: T, key: keyof S, currentIndex: number, array: string[], subject: S) => T, 6 | initialValue: T, 7 | ): T { 8 | return Object.keys(subject).reduce((p, k, i, a) => callbackfn(p, k, i, a, subject), initialValue) 9 | } 10 | 11 | /** 12 | * @deprecated renamed to reduceByKey 13 | */ 14 | export const reduceKey = reduceByKey 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/replaceProperty.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { type ReplaceProperty, assertType, replaceProperty } from '../index.js' 4 | 5 | test('replaceProperty()', () => { 6 | const subject = { a: 1, b: 2 } as const 7 | const actual = replaceProperty(subject, 'a', () => 1) 8 | assertType<{ a: () => 1; b: 2 }>(actual) 9 | expect(actual.a()).toBe(1) 10 | }) 11 | 12 | test('ReplaceProperty<>', () => { 13 | const subject = { a: 1, b: 2 } 14 | 15 | const actual = subject as ReplaceProperty 16 | 17 | assertType<{ a: 1; b: number }>(actual) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/replaceProperty.ts: -------------------------------------------------------------------------------- 1 | import type { AnyRecord } from './any_record.js' 2 | 3 | export type ReplaceProperty = Omit & { [P in K]: V } 4 | 5 | export function replaceProperty( 6 | subject: T, 7 | key: K, 8 | value: V, 9 | ): ReplaceProperty { 10 | return { ...subject, [key]: value } 11 | } 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/someKey.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it, test } from '@jest/globals' 2 | 3 | import { isType, someKey } from '../index.js' 4 | 5 | test('predicate key can be used as indexer of the subject', () => { 6 | const subject = { a: 1, b: 2, c: 3 } 7 | expect(someKey(subject, (key) => subject[key] === 2)).toBe(true) 8 | }) 9 | 10 | it('includes subject in callback', () => { 11 | const subject = { a: 1, b: 2, c: 3 } 12 | const actual = someKey(subject, (k, _i, _a, s) => s[k] === 2) 13 | isType.equal() 14 | expect(actual).toBe(true) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/someKey.ts: -------------------------------------------------------------------------------- 1 | import type { KeyTypes } from './KeyTypes.js' 2 | 3 | export function someKey, T = any>( 4 | subject: S, 5 | predicate: (this: T, key: keyof S, index: number, array: string[], subject: S) => unknown, 6 | thisArg?: T, 7 | ): boolean { 8 | return Object.keys(subject).some(function (this: T, k, i, a) { 9 | return predicate.apply(this, [k, i, a, subject]) 10 | }, thisArg) 11 | } 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/typeOverrideIncompatible.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { typeOverrideIncompatible } from '../index.js' 4 | 5 | test('same type override has no property', () => { 6 | type A = { a: 1 } 7 | const transform = typeOverrideIncompatible() 8 | 9 | // TODO: find a way to assert `override` has no property 10 | transform({ a: 1 }, {}) 11 | }) 12 | 13 | test('disjoint type override is A', () => { 14 | type A = { a: 1 } 15 | const transform = typeOverrideIncompatible() 16 | 17 | transform({ b: 2 }, { a: 1 }) 18 | }) 19 | 20 | test('intersect type override is ANotB', () => { 21 | const a = { a: 1, b: 2 } as const 22 | const b = { b: 3, c: 3 } as const 23 | const transform = typeOverrideIncompatible() 24 | 25 | transform(b, { a: 1, b: 2 }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/type-plus/src/object/typeOverrideIncompatible.ts: -------------------------------------------------------------------------------- 1 | import type { ANotB, AnyRecord } from './index.js' 2 | 3 | export function typeOverrideIncompatible() { 4 | return (source: B, override: ANotB): A => ({ 5 | ...source, 6 | ...override, 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/Extends.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @deprecated use `$Assignable` 3 | */ 4 | export type Extendable = A extends B ? Then : Else 5 | export type NotExtendable = A extends B ? Else : Then 6 | export type IsExtend = A extends B ? Then : Else 7 | export type IsNotExtend = A extends B ? Else : Then 8 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/If.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type If, assertType, isType } from '../index.js' 4 | 5 | test('true gets Then', () => { 6 | assertType>(2) 7 | }) 8 | 9 | test('false gets Else', () => { 10 | assertType>(3) 11 | }) 12 | 13 | test('Then defaults to true and Else defaults to false', () => { 14 | isType.t>() 15 | isType.f>() 16 | }) 17 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/If.ts: -------------------------------------------------------------------------------- 1 | export type If = Condition extends true ? Then : Else 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/IsEmptyObject.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type IsEmptyObject, testType } from '../index.js' 4 | 5 | test('true for {}', () => { 6 | testType.true>(true) 7 | }) 8 | test('false for everything else', () => { 9 | testType.false>(true) 10 | testType.false>(true) 11 | testType.false>(true) 12 | testType.false>(true) 13 | testType.false>(true) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/IsEmptyObject.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-types */ 2 | export type IsEmptyObject = T extends {} ? ({} extends T ? true : false) : false 3 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, test } from '@jest/globals' 2 | 3 | import { type IsBoolean, isType } from '../index.js' 4 | 5 | describe('IsBoolean', () => { 6 | test('boolean/true/false', () => { 7 | isType.equal>() 8 | isType.equal>() 9 | isType.equal>() 10 | }) 11 | test('override Then/Else', () => { 12 | isType.equal>() 13 | isType.equal>() 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CanAssign.js' 2 | export type { Extendable, IsExtend, IsNotExtend, NotExtendable } from './Extends.js' 3 | export type { If } from './If.js' 4 | export type { IsEmptyObject } from './IsEmptyObject.js' 5 | export type { IsLiteral } from './literal.js' 6 | export type { And, Not, Or, Xor } from '../logical/logical.js' 7 | -------------------------------------------------------------------------------- /packages/type-plus/src/predicates/literal.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Validate if specified type is a scalar literal. 3 | * 4 | * 🎭 *predicate* 5 | * 6 | * @example 7 | * ```ts 8 | * type R = IsLiteral // false 9 | * type R = IsLiteral // false 10 | * type R = IsLiteral // false 11 | * type R = IsLiteral // false 12 | * type R = IsLiteral // false 13 | * 14 | * type R = IsLiteral<'a'> // true 15 | * type R = IsLiteral<1> // true 16 | * type R = IsLiteral // true 17 | * type R = IsLiteral<1n> // true 18 | * type R = IsLiteral // true 19 | * ``` 20 | */ 21 | export type IsLiteral< 22 | T extends number | boolean | bigint | string | symbol, 23 | Then = true, 24 | Else = false, 25 | > = number extends T 26 | ? Else 27 | : string extends T 28 | ? Else 29 | : boolean extends T 30 | ? Else 31 | : symbol extends T 32 | ? Else 33 | : bigint extends T 34 | ? Else 35 | : Then 36 | -------------------------------------------------------------------------------- /packages/type-plus/src/primitive.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it } from '@jest/globals' 2 | 3 | import { type IsNever, isType } from './index.js' 4 | 5 | describe('IsNever', () => { 6 | it('checks if type is never', () => { 7 | isType.equal>() 8 | isType.equal>() 9 | isType.equal>() 10 | isType.equal>() 11 | isType.equal>() 12 | isType.equal>() 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/primitive.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 📘 Definition of all primitive types. 3 | */ 4 | export type PrimitiveTypes = boolean | number | string | object | symbol | bigint | Function | undefined | null 5 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/MaybePromise.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from '@jest/globals' 2 | 3 | import { MaybePromise, isType } from '../index.js' 4 | 5 | describe(`${MaybePromise.transform.name}()`, () => { 6 | it('returns the result from the handler directly if it is not a promise', () => { 7 | const r = MaybePromise.transform(1, (v) => String(v)) 8 | 9 | isType.equal() 10 | expect(r).toEqual('1') 11 | }) 12 | 13 | it('returns a promise that resolves to the result from the handler', async () => { 14 | const r = MaybePromise.transform(Promise.resolve(1), (v) => String(v)) 15 | 16 | isType.equal, typeof r>() 17 | expect(r).not.toEqual('1') 18 | expect(await r).toEqual('1') 19 | }) 20 | 21 | it('preserves the type of it is MaybePromise', () => { 22 | const v: MaybePromise = 1 as MaybePromise 23 | const r = MaybePromise.transform(v, (v) => String(v)) 24 | isType.equal, typeof r>() 25 | expect(r).toEqual('1') 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/PromiseValue.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, test } from '@jest/globals' 2 | 3 | import { type AwaitedProp, type PromiseValue, assertType, isType } from '../index.js' 4 | 5 | test('extract value from Promise', () => { 6 | const y: PromiseValue> = '' 7 | assertType.isString(y) 8 | }) 9 | 10 | describe('AwaitedProp', () => { 11 | it('awaits one of the props', () => { 12 | type E = { a: number; p: Promise } 13 | type R = AwaitedProp 14 | isType.equal() 15 | }) 16 | 17 | it('awaits multiple props', () => { 18 | type E = { 19 | a: number 20 | p1: Promise 21 | p2: Promise 22 | p3: Promise 23 | } 24 | type R = AwaitedProp 25 | isType.equal< 26 | true, 27 | { 28 | a: number 29 | p1: number 30 | p2: number 31 | p3: Promise 32 | }, 33 | R 34 | >() 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/PromiseValue.ts: -------------------------------------------------------------------------------- 1 | import type { AnyRecord } from '../index.js' 2 | 3 | /** 4 | * Gets value type from Promise 5 | * @deprecated Use `Awaited` instead. 6 | */ 7 | export type PromiseValue

> = P extends Promise ? T : never 8 | 9 | /** 10 | * Await on specific props V on type T 11 | */ 12 | export type AwaitedProp = { 13 | [k in keyof T]: k extends K ? Awaited : T[k] 14 | } 15 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/PromiseValueMerge.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type PromiseValueMerge, assertType } from '../index.js' 4 | 5 | test('merge promise value', async () => { 6 | const result = {} as PromiseValueMerge, Promise<{ b: string }>> 7 | const value = await result 8 | assertType<{ a: string; b: string }>(value) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/PromiseValueMerge.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Merging value types from multiple promises. 3 | */ 4 | export type PromiseValueMerge< 5 | P1 extends Promise, 6 | P2 extends Promise, 7 | P3 extends Promise = any, 8 | P4 extends Promise = any, 9 | P5 extends Promise = any, 10 | P6 extends Promise = any, 11 | P7 extends Promise = any, 12 | P8 extends Promise = any, 13 | P9 extends Promise = any, 14 | > = Promise< 15 | Awaited & 16 | Awaited & 17 | Awaited & 18 | Awaited & 19 | Awaited & 20 | Awaited & 21 | Awaited & 22 | Awaited & 23 | Awaited 24 | > 25 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/index.ts: -------------------------------------------------------------------------------- 1 | export * from './isPromise.js' 2 | export * from './mapSeries.js' 3 | export * from './MaybePromise.js' 4 | export type { AwaitedProp, PromiseValue } from './PromiseValue.js' 5 | export type { PromiseValueMerge } from './PromiseValueMerge.js' 6 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/isPromise.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { isPromise } from '../index.js' 4 | 5 | test('false if subject is falsy value or non-object', () => { 6 | expect(isPromise(undefined)).toBe(false) 7 | expect(isPromise(null)).toBe(false) 8 | expect(isPromise(0)).toBe(false) 9 | expect(isPromise(true)).toBe(false) 10 | expect(isPromise('a')).toBe(false) 11 | expect(isPromise([])).toBe(false) 12 | }) 13 | 14 | test('false if subject does not have a then function', () => { 15 | expect(isPromise({})).toBe(false) 16 | }) 17 | 18 | test('false if subject.then is not a function', () => { 19 | // biome-ignore lint/suspicious/noThenProperty: on purpose 20 | expect(isPromise({ then: true })).toBe(false) 21 | }) 22 | 23 | test('type guard as promise', () => { 24 | const subject = { 25 | // biome-ignore lint/suspicious/noThenProperty: on purpose 26 | then() { 27 | return true 28 | }, 29 | } 30 | if (isPromise(subject)) { 31 | expect(subject.then()).toBe(true) 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/isPromise.ts: -------------------------------------------------------------------------------- 1 | export function isPromise(subject: unknown): subject is Promise { 2 | // @ts-ignore 3 | return !!subject && typeof subject['then'] === 'function' 4 | } 5 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/mapSeries.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@jest/globals' 2 | 3 | import { mapSeries } from '../index.js' 4 | 5 | test('map over values', () => { 6 | const values = [3, 2, 1] 7 | let actual = '' 8 | 9 | return mapSeries(values, (v) => new Promise((a) => setTimeout(() => a((actual += v)), v * 10))).then( 10 | (result) => { 11 | expect(actual).toBe('321') 12 | expect(result).toEqual(['3', '32', '321']) 13 | }, 14 | ) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/promise/mapSeries.ts: -------------------------------------------------------------------------------- 1 | export function mapSeries(values: T[], fn: (value: T) => Promise): Promise { 2 | return values.reduce((p, v) => p.then((r) => fn(v).then((v) => (r.push(v), r))), Promise.resolve([])) 3 | } 4 | -------------------------------------------------------------------------------- /packages/type-plus/src/string/_string_type.ts: -------------------------------------------------------------------------------- 1 | import type { $ExtractManipulatedString } from './$extract_manipulated_string.js' 2 | 3 | export type _StringType = $ExtractManipulatedString extends infer K 4 | ? K extends string & infer U 5 | ? [K, U] extends [U, K] 6 | ? {} extends { [P in `${K}`]: unknown } 7 | ? 'templateLiteral' 8 | : 'stringLiteral' 9 | : 'string' 10 | : never 11 | : never 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/string/string.split.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type StringSplit, testType } from '../index.js' 4 | 5 | it('should split a string into an array of substrings', () => { 6 | testType.equal, ['a', '']>(true) 7 | testType.equal, ['', 'b']>(true) 8 | 9 | testType.equal, ['a', 'b', 'c']>(true) 10 | testType.equal, ['a', 'c']>(true) 11 | testType.equal, ['abc']>(true) 12 | testType.equal, ['', 'bc']>(true) 13 | testType.equal, ['ab', '']>(true) 14 | 15 | testType.equal, ['a', 'b', 'c']>(true) 16 | testType.equal, ['a', 'b', 'c', '']>(true) 17 | testType.equal, ['', 'a', 'b', 'c']>(true) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/type-plus/src/string/string_plus.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type StringIncludes, type StringPlus, testType } from '../index.js' 4 | 5 | it('exposes Includes', () => { 6 | testType.equal, StringIncludes<'', ''>>(true) 7 | }) 8 | 9 | it('exposes Split', () => { 10 | testType.equal, ['a', 'b', 'c']>(true) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/stub.build.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from '@jest/globals' 2 | 3 | import { stub, testType } from '../index.js' 4 | 5 | it('can specify init as value', () => { 6 | type S = { a: number; b: string } 7 | const s = stub.build({ b: 'b' }) 8 | const a = s({ a: 1 }) 9 | expect(a).toEqual({ a: 1, b: 'b' }) 10 | testType.equal(true) 11 | }) 12 | 13 | it('accepts a init function', () => { 14 | let count = 0 15 | const s = stub.build<{ a: number; b: string }>(() => ({ a: count++ })) 16 | expect(s()).toEqual({ a: 0 }) 17 | expect(s()).toEqual({ a: 1 }) 18 | }) 19 | 20 | it('pass stub input to the init function', () => { 21 | const s = stub.build<{ a: number; b: string }>((stub) => ({ a: (stub?.a ?? 0) + 1 })) 22 | expect(s({ a: 1 })).toEqual({ a: 2 }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/stub.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from '@jest/globals' 2 | 3 | import { stub, testType } from '../index.js' 4 | 5 | it('accepts a partial of the requested type', () => { 6 | function foo(_v: { a: number; b: string }) {} 7 | 8 | foo(stub({ a: 1 })) 9 | }) 10 | 11 | it('pass through the stub if it is a function', () => { 12 | // this is useful when the stub is being used generically, 13 | // and the value can be a function. 14 | const r = stub(() => ({ a: 1 })) 15 | testType.equal { a: number }>(true) 16 | expect(r()).toEqual({ a: 1 }) 17 | }) 18 | 19 | it('retains param types if the requested type is a function', () => { 20 | // This is the use case for `NoInfer` 21 | function foo(_v: (a: string, b: number) => boolean) {} 22 | 23 | foo( 24 | stub((a, b) => { 25 | testType.equal(true) 26 | testType.equal(true) 27 | return false 28 | }), 29 | ) 30 | }) 31 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/test_type.array.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { testType } from './test_type.js' 4 | 5 | it('accepts array', () => { 6 | testType.array(true) 7 | testType.array>(true) 8 | }) 9 | 10 | it('rejects tuples', () => { 11 | testType.array<[]>(false) 12 | testType.array<[number]>(false) 13 | }) 14 | 15 | it('treat special types as not true', () => { 16 | testType.array(false) 17 | testType.array(false) 18 | testType.array(false) 19 | testType.array(false) 20 | }) 21 | 22 | it('treat all other types as not true', () => { 23 | testType.array(false) 24 | testType.array(false) 25 | testType.array(false) 26 | testType.array(false) 27 | testType.array<''>(false) 28 | testType.array(false) 29 | testType.array(false) 30 | testType.array<{}>(false) 31 | testType.array<[]>(false) 32 | testType.array(false) 33 | testType.array<() => void>(false) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/test_type.can_assign.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | 5 | it('should pass if A can assign to B', () => { 6 | testType.canAssign(true) 7 | testType.canAssign(true) 8 | }) 9 | 10 | it('should pass if A is a literal of B', () => { 11 | testType.canAssign<123, number>(true) 12 | }) 13 | 14 | it('accepts both true/false if A is a union an partially assignable to B', () => { 15 | testType.canAssign(true) 16 | testType.canAssign(false) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/test_type.null.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { testType } from './test_type.js' 4 | 5 | it('accepts null', () => { 6 | testType.null(true) 7 | }) 8 | 9 | it('rejects undefined', () => { 10 | testType.null(false) 11 | }) 12 | 13 | it('treat special types as not true', () => { 14 | testType.null(false) 15 | testType.null(false) 16 | testType.null(false) 17 | testType.null(false) 18 | }) 19 | 20 | it('treat all other types as not true', () => { 21 | testType.null(false) 22 | testType.null(false) 23 | testType.null(false) 24 | testType.null<''>(false) 25 | testType.null(false) 26 | testType.null(false) 27 | testType.null<{}>(false) 28 | testType.null(false) 29 | testType.null<[]>(false) 30 | testType.null(false) 31 | testType.null<() => void>(false) 32 | }) 33 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/test_type.strict_boolean.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | 5 | it('accepts boolean', () => { 6 | testType.strictBoolean(true) 7 | }) 8 | 9 | it('rejects boolean literal', () => { 10 | testType.strictBoolean(false) 11 | testType.strictBoolean(false) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/test_type.strict_can_assign.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | 5 | it('should pass if A can assign to B', () => { 6 | testType.strictCanAssign(true) 7 | testType.strictCanAssign(true) 8 | }) 9 | 10 | it('should pass if A is a literal of B', () => { 11 | testType.strictCanAssign<123, number>(true) 12 | }) 13 | 14 | it('should pass only if A is fully assignable to B across all branches', () => { 15 | testType.strictCanAssign(true) 16 | 17 | testType.strictCanAssign(false) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/test_type.unknown.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | 5 | it('accepts unknown', () => { 6 | testType.unknown(true) 7 | }) 8 | 9 | it('rejects others', () => { 10 | testType.unknown(false) 11 | testType.unknown(false) 12 | testType.unknown(false) 13 | 14 | testType.unknown(false) 15 | testType.unknown(false) 16 | testType.unknown(false) 17 | testType.unknown(false) 18 | testType.unknown(false) 19 | testType.unknown(false) 20 | testType.unknown<1>(false) 21 | testType.unknown(false) 22 | testType.unknown<1n>(false) 23 | testType.unknown(false) 24 | testType.unknown<''>(false) 25 | testType.unknown(false) 26 | testType.unknown(false) 27 | testType.unknown<{}>(false) 28 | testType.unknown(false) 29 | testType.unknown<[]>(false) 30 | testType.unknown(false) 31 | testType.unknown<() => void>(false) 32 | }) 33 | -------------------------------------------------------------------------------- /packages/type-plus/src/testing/test_type.void.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | 5 | it('accepts void', () => { 6 | testType.void(true) 7 | }) 8 | 9 | it('rejects others', () => { 10 | testType.void(false) 11 | testType.void(false) 12 | testType.void(false) 13 | 14 | testType.void(false) 15 | testType.void(false) 16 | testType.void(false) 17 | testType.void(false) 18 | testType.void(false) 19 | testType.void(false) 20 | testType.void<1>(false) 21 | testType.void(false) 22 | testType.void<1n>(false) 23 | testType.void(false) 24 | testType.void<''>(false) 25 | testType.void(false) 26 | testType.void(false) 27 | testType.void<{}>(false) 28 | testType.void(false) 29 | testType.void<[]>(false) 30 | testType.void(false) 31 | testType.void<() => void>(false) 32 | }) 33 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/drop_first.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type DropFirst, testType } from '../index.js' 4 | 5 | it('returns the input type if input is an array', () => { 6 | testType.equal, string[]>(true) 7 | 8 | const s: string[] = ['a', 'b', 'c'] 9 | type Actual = DropFirst 10 | testType.equal(true) 11 | }) 12 | 13 | it('drops the first type from a tuple', () => { 14 | testType.equal, [1, 'x', 3]>(true) 15 | }) 16 | 17 | it('returns empty tuple [] when dropping from single entry tuple', () => { 18 | testType.equal, []>(true) 19 | }) 20 | 21 | it('returns empty tuple [] if input is empty tuple', () => { 22 | testType.equal, []>(true) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/drop_last.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type DropLast, testType } from '../index.js' 4 | 5 | it('returns the input type if input is an array', () => { 6 | testType.equal, string[]>(true) 7 | const s: string[] = ['a', 'b', 'c'] 8 | type Actual = DropLast 9 | testType.equal(true) 10 | }) 11 | 12 | it('drop first value type from tuple', () => { 13 | testType.equal, [true, 1, 'x']>(true) 14 | }) 15 | 16 | it('returns empty tuple [] when dropping from single entry tuple', () => { 17 | testType.equal, []>(true) 18 | }) 19 | 20 | it('returns empty tuple [] if input is empty tuple', () => { 21 | testType.equal, []>(true) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tail.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { type Tail, testType } from '../index.js' 4 | 5 | test('get tail types', () => { 6 | type S = [1, 'a', 'b'] 7 | type A = Tail 8 | 9 | testType.equal(true) 10 | }) 11 | 12 | test('empty tuple gets never', () => { 13 | type S = [] 14 | type A = Tail 15 | 16 | testType.never(true) 17 | }) 18 | 19 | test('array gets same type', () => { 20 | type A = Tail 21 | 22 | testType.equal(true) 23 | }) 24 | 25 | test('union array', () => { 26 | type A = Tail> 27 | 28 | testType.equal>(true) 29 | }) 30 | 31 | test('tuple with rest', () => { 32 | type A = Tail<[number, ...string[]]> 33 | testType.equal(true) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tail.ts: -------------------------------------------------------------------------------- 1 | import type { UnionOfValues } from '../array/union_of_values.js' 2 | 3 | /** 4 | * Gets the types of a tuple except the first entry. 5 | */ 6 | export type Tail = T['length'] extends 0 7 | ? never 8 | : T extends readonly [any, ...infer Tail] 9 | ? Tail extends UnionOfValues[] 10 | ? Tail 11 | : never 12 | : T 13 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tuple_plus.drop_match.ts: -------------------------------------------------------------------------------- 1 | import type { IsEqual } from '../equal/is_equal.js' 2 | 3 | export type DropMatch>, Criteria> = A['length'] extends 0 4 | ? // empty tuple 5 | A 6 | : A extends readonly [infer Head, ...infer Tail] 7 | ? Tail['length'] extends 0 8 | ? // single element tuple 9 | undefined extends Criteria 10 | ? DropMatch.ExcludeUnionOfEmptyTuple 11 | : DropMatch.ExcludeUnionOfEmptyTuple 12 | : // multiple elements 13 | Exclude extends never 14 | ? DropMatch 15 | : [Exclude, ...DropMatch] 16 | : never[] 17 | 18 | export namespace DropMatch { 19 | export type ExcludeUnionOfEmptyTuple = IsEqual extends true ? A : Exclude 20 | } 21 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tuple_plus.filter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * ⚗️ *transform* 3 | * 4 | * Filter entries matching `Criteria` in tuple `T`. 5 | * 6 | * @example 7 | * ```ts 8 | * type R = Filter<[1, 2, '3'], number> // [1, 2] 9 | * type R = Filter<[1, 2, '3'], true> // [] 10 | * ``` 11 | */ 12 | export type Filter = T['length'] extends 0 13 | ? [] 14 | : T extends readonly [infer Head, ...infer Tail] 15 | ? Tail['length'] extends 0 16 | ? Head extends Criteria 17 | ? [Head] 18 | : [] 19 | : Head extends Criteria 20 | ? [Head, ...Filter] 21 | : Filter 22 | : never 23 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tuple_plus.find.ts54.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | 5 | test('behavior of tuple.find()', () => { 6 | const tuple: [1, 2, '3'] = [1, 2, '3'] 7 | const r = tuple.find((x) => typeof x === 'number') 8 | testType.equal(true) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tuple_plus.find.ts55.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@jest/globals' 2 | 3 | import { testType } from '../index.js' 4 | 5 | test('behavior of tuple.find()', () => { 6 | const tuple: [1, 2, '3'] = [1, 2, '3'] 7 | const r = tuple.find((x) => typeof x === 'number') 8 | testType.equal(true) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tuple_plus.pad_start.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type TuplePlus, testType } from '../index.js' 4 | 5 | it('pads with unknown', () => { 6 | testType.equal, [unknown, unknown, 1, 2, 3]>(true) 7 | }) 8 | 9 | it('returns source type if total length is less than source length', () => { 10 | testType.equal, [1, 2, 3]>(true) 11 | }) 12 | 13 | it('can specify what to pad with', () => { 14 | testType.equal, [0, 0, 1, 2, 3]>(true) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tuple_plus.ts: -------------------------------------------------------------------------------- 1 | export type { CommonPropKeys } from './tuple_plus.common_prop_keys.js' 2 | export type { DropMatch } from './tuple_plus.drop_match.js' 3 | export type { Filter } from './tuple_plus.filter.js' 4 | export type { Find } from './tuple_plus.find.js' 5 | export type { PadStart } from './tuple_plus.pad_start.js' 6 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/tuple_type.distributive.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type IsNever, testType } from '../index.js' 4 | 5 | // @todo: this should be a new type that filter within the union. 6 | // That type cannot support override because the `Then` type can be overridden, 7 | // and the logic will not be correct. 8 | type TupleTypeFilterUnion = IsNever< 9 | T, 10 | { 11 | $then: never 12 | $else: T extends any[] ? (number extends T['length'] ? never : T) : never 13 | } 14 | > 15 | 16 | it.skip('returns filtered union if T is an union of tuple with non-tuple types', () => { 17 | testType.equal, [1]>(true) 18 | testType.equal, [1] | [1, 2]>(true) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/type-plus/src/tuple/type_plus.filter.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type TuplePlus, testType } from '../index.js' 4 | 5 | it('filters empty tuple -> empty tuple', () => { 6 | testType.equal, []>(true) 7 | }) 8 | 9 | it('filters for true elements by default', () => { 10 | testType.equal, [true, true]>(true) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/type-guard/is_type.never.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { isType } from '../index.js' 4 | 5 | it('can be used for type-only', () => { 6 | isType.never() 7 | 8 | // @ts-expect-error 9 | isType.never() 10 | }) 11 | 12 | it('can be used for value', () => { 13 | isType.never({} as never) 14 | 15 | // @ts-expect-error 16 | isType.never(undefined) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/type-plus/src/type-guard/readme.md: -------------------------------------------------------------------------------- 1 | # Type Guard 2 | 3 | [User-defined type guard functions][type_guard] is a function which its return type is specified as `x as T`. 4 | 5 | It is introduced in TypeScript 1.6. 6 | 7 | For example: 8 | 9 | ```ts 10 | function isBool(x: unknown): x is boolean { 11 | return typeof x === 'boolean'; 12 | } 13 | ``` 14 | 15 | ## isType 16 | 17 | > `isType(subject: T): subject is T` 18 | 19 | ✔️ `immediate` 20 | 21 | It ensures `subject` satisfies `T`. 22 | You need to specify `T`. 23 | 24 | > `isType(subject: unknown, validator: (s: T) => unknown): subject is T` 25 | 26 | It is a generic type guard. 27 | You can use it for do quick type guard without creating a custom one. 28 | 29 | ```ts 30 | const s: unknown = 1 31 | 32 | if (isType<1>(s, v => v === 1)) { 33 | // s is narrowed to type `1` here. 34 | } 35 | ``` 36 | 37 | [type_guard]: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates 38 | -------------------------------------------------------------------------------- /packages/type-plus/src/union/sub_union.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 🧰 *type util* 3 | * Define a union type that is a subset of union type. 4 | */ 5 | export type SubUnion = T 6 | -------------------------------------------------------------------------------- /packages/type-plus/src/union/union.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 🌪️ *filter* 3 | * 4 | * Filter the type `T` to ensure it is a union. 5 | * 6 | * @example 7 | * ```ts 8 | * type R = IsUnion<'a' | 'b'> // 'a' | 'b' 9 | * type R = IsUnion // boolean 10 | * type R = IsUnion // never 11 | * ``` 12 | */ 13 | export type UnionType = UnionType.Device 14 | 15 | /** 16 | * 🎭 *predicate* 17 | * 18 | * Validate the type `T` is a union. 19 | * 20 | * @author Nurbol Alpysbayev 21 | * @see https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union 22 | * 23 | * @example 24 | * ```ts 25 | * type R = IsUnion<'a' | 'b'> // true 26 | * type R = IsUnion // true 27 | * type R = IsUnion // false 28 | * ``` 29 | */ 30 | export type IsUnion = UnionType.Device 31 | 32 | export namespace UnionType { 33 | export type Device = (T extends unknown ? (U extends T ? 1 : 2) : never) extends 1 ? Else : Then 34 | } 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/union/union_to_intersection.ts: -------------------------------------------------------------------------------- 1 | // from jcalz, https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type/50375286#50375286 2 | // istanbul ignore file 3 | export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never 4 | 5 | export function toIntersection(union: U): UnionToIntersection { 6 | return union as any as UnionToIntersection 7 | } 8 | -------------------------------------------------------------------------------- /packages/type-plus/src/union_keys.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from '@jest/globals' 2 | import { type UnionKeys, testType } from './index.js' 3 | 4 | it('gets the keys of an object', () => { 5 | testType.equal, 'a' | 'b'>(true) 6 | }) 7 | 8 | it('returns known keys', () => { 9 | type Foo = { 10 | a: string 11 | b: string 12 | } 13 | 14 | // @ts-ignore 15 | function _foo(_input: UnionKeys): void { 16 | _input = 'a' 17 | _input = 'b' 18 | // @ts-expect-error 19 | _input = 'c' 20 | 21 | let r: keyof (Foo & T) 22 | r = 'a' 23 | r = 'b' 24 | // @ts-expect-error 25 | r = 'c' 26 | expect(r).toBe('c') 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /packages/type-plus/src/union_keys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `UnionKeys` will distribute keys of an union to individual types. 3 | * This should be used in conjuncture with distributive types. 4 | */ 5 | export type UnionKeys = keyof T | (T extends unknown ? keyof T : never) 6 | -------------------------------------------------------------------------------- /packages/type-plus/src/unknown/not_unknown_or.spec.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type $Unknown, type NotUnknownOr, testType } from '../index.js' 4 | 5 | it('pass through if not unknown', () => { 6 | testType.equal, number>(true) 7 | testType.equal, boolean | string>(true) 8 | }) 9 | 10 | it('returns $Unknown if unknown', () => { 11 | testType.equal, $Unknown>(true) 12 | 13 | type R = NotUnknownOr extends $Unknown ? 1 : 2 14 | testType.equal, 1>(true) 15 | testType.equal, 2>(true) 16 | }) 17 | 18 | it('can override Else branch if the branch is simple', () => { 19 | // Do this only when the branch is simple to avoid performance issue. 20 | testType.equal, 1>(true) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/type-plus/src/unknown/not_unknown_or.ts: -------------------------------------------------------------------------------- 1 | import type { $Unknown } from '../$type/special/$unknown.js' 2 | import type { IsUnknown } from './is_unknown.js' 3 | 4 | /** 5 | * 🌪️ *filter* 6 | * 7 | * Returns `T` if `T` is not `unknown`, otherwise `$Unknown`. 8 | * 9 | * @example 10 | * ```ts 11 | * type R = NotUnknownOr // number 12 | * type R = NotUnknownOr // $Unknown 13 | * 14 | * // customize 15 | * type R = NotUnknownOr // number 16 | * ``` 17 | * 18 | * 🔢 *customize* 19 | * 20 | * Replace `unknown` branch with `Replace`. 21 | * 22 | * @example 23 | * ```ts 24 | * type R = NotUnknownOr // number 25 | * ``` 26 | */ 27 | export type NotUnknownOr = IsUnknown< 28 | T, 29 | { 30 | $then: Else 31 | $else: T 32 | } 33 | > 34 | -------------------------------------------------------------------------------- /packages/type-plus/src/unpartial.ts: -------------------------------------------------------------------------------- 1 | export { required, requiredDeep, unpartial } from 'unpartial' 2 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/Widen.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * ⚗️ *transform* 3 | * 4 | * Widen literal types to their respective parent types. 5 | * 6 | * ```ts 7 | * type R = Widen<1> // number 8 | * type R = Widen // boolean 9 | * type R = Widen<'a'> // string 10 | * ``` 11 | */ 12 | export type Widen = T extends boolean ? boolean : T extends number ? number : T extends string ? string : T 13 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/as.ts: -------------------------------------------------------------------------------- 1 | export function as(subject: unknown): T { 2 | return subject as T 3 | } 4 | 5 | export function asAny(subject: unknown): any { 6 | return subject 7 | } 8 | 9 | /** 10 | * amend `subject` with type `T` 11 | */ 12 | export function amend(subject: S) { 13 | return { 14 | union(): T & S { 15 | return subject as T & S 16 | }, 17 | intersect(): T | S { 18 | return subject as T | S 19 | }, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/excessive.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 🧰 *type util* 3 | * 4 | * A type that causes the error: 5 | * 6 | * `Type instantiation is excessively deep and possibly infinite.ts(2589)` 7 | * 8 | * This is used interally to test type path execution. 9 | * It is not exported and likely not useful in real world. 10 | */ 11 | export type Excessive = T extends any ? Excessive : never 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/inspect.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import { expect, it, jest } from '@jest/globals' 3 | 4 | import { inspect } from '../index.js' 5 | 6 | it('should return the same value', () => { 7 | const value = { a: 1, b: 2 } 8 | expect(inspect(value, () => {})).toBe(value) 9 | }) 10 | it('should call the inspector', () => { 11 | const value = { a: 1, b: 2 } 12 | const spy = jest.fn() 13 | inspect(value, spy) 14 | expect(spy).toBeCalledWith(value) 15 | }) 16 | it('defaults to console.dir', () => { 17 | const value = { a: 1, b: 2 } 18 | const dir = console.dir 19 | console.dir = jest.fn() 20 | inspect(value) 21 | expect(console.dir).toBeCalledWith(value) 22 | console.dir = dir 23 | }) 24 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/inspect.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | /** 4 | * Inspects the value and returns it. 5 | */ 6 | export function inspect(value: T, inspector: (value: Readonly) => void = console.dir): T { 7 | inspector(value) 8 | return value 9 | } 10 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/no_infer.ts: -------------------------------------------------------------------------------- 1 | import type { $Selection } from '../$type/branch/$selection.js' 2 | import type { Or } from '../logical/logical.js' 3 | import type { IsNull } from '../null/is_null.js' 4 | import type { IsUndefined } from '../undefined/is_undefined.js' 5 | 6 | /** 7 | * Prevents inference of a type parameter `T`. 8 | * 9 | * 🦴 *utilities* 10 | * 11 | * @deprecated 💀 **deprecated since 8.0.0**: use `NoInfer` from TypeScript 5.4 instead 12 | * 13 | * @author original version author @ajafff 14 | * @see https://github.com/microsoft/TypeScript/issues/14829#issuecomment-298425341 15 | * 16 | * @example 17 | * ```ts 18 | * function assertEqual(a: T, b: NoInfer) { 19 | * return a === b 20 | * } 21 | * 22 | * assertEqual(123, 324) // OK 23 | * assertEqual(123, 'abc') // Error 24 | * assertEqual({ x: 1 }, { x: 1, y: 2 }) // Error 25 | * ``` 26 | */ 27 | export type NoInfer = Or< 28 | IsNull, 29 | IsUndefined, 30 | { 31 | $then: T 32 | $else: T & {} 33 | } 34 | > 35 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/options.merge.unit.ts: -------------------------------------------------------------------------------- 1 | import { it } from '@jest/globals' 2 | 3 | import { type TypePlusOptions, testType } from '../index.js' 4 | 5 | it('overrides cases', () => { 6 | testType.equal, { a: 3; b: 2 }>(true) 7 | }) 8 | 9 | it('can specify input value as never', () => { 10 | testType.equal, { a: never; b: 2 }>(true) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/options.ts: -------------------------------------------------------------------------------- 1 | export namespace TypePlusOptions { 2 | /** 3 | * 🦴 *utilities* 4 | * ㊙️ *internal* 5 | * 6 | * Merge the input Options `I` with the default Options `D`. 7 | */ 8 | export type Merge = { 9 | [k in keyof D]: k extends keyof I ? I[k] : D[k] 10 | } 11 | 12 | export interface NotArray { 13 | $notArray?: unknown 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/type-plus/src/utils/readme.md: -------------------------------------------------------------------------------- 1 | # Utilities 2 | 3 | ## [`NoInfer`](./no_infer.ts) 4 | 5 | 💀 **deprecated 8.0.0**: use the built-in `NoInfer` from TypeScript 5.4 instead. 6 | 7 | `NoInfer` is a type to prevent type inference. 8 | 9 | ```ts 10 | import { type NoInfer } from 'type-plus' 11 | 12 | function assertEqual(a: T, b: NoInfer) { 13 | return a === b 14 | } 15 | 16 | function stub(v: RecursivePartial>): T { return v } 17 | ``` 18 | -------------------------------------------------------------------------------- /packages/type-plus/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repobuddy/typescript/tsconfig/monorepo", 3 | "compilerOptions": { 4 | "composite": false, 5 | "rootDir": "src", 6 | "skipLibCheck": true 7 | }, 8 | "exclude": ["node_modules", "src/**/*.ts*.spec.ts"], 9 | "include": ["src", "types"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/type-plus/tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "outDir": "cjs", 6 | "verbatimModuleSyntax": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/type-plus/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "esm", 5 | "verbatimModuleSyntax": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/type-plus/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.ts56.json", 3 | "typedocOptions": { 4 | "entryPoints": ["src/index.ts"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/type-plus/tsconfig.ts54.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.esm.json", 3 | "exclude": ["node_modules", "src/**/*.ts55.spec.ts", "src/**/*.ts56.spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/type-plus/tsconfig.ts55.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.esm.json", 3 | "exclude": ["node_modules", "src/**/*.ts54.spec.ts", "src/**/*.ts56.spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/type-plus/tsconfig.ts56.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.esm.json", 3 | "exclude": ["node_modules", "src/**/*.ts54.spec.ts", "src/**/*.ts55.spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - apps/* 3 | - packages/* 4 | -------------------------------------------------------------------------------- /slides/_revealjs/uni.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unional/type-plus/07ccb2ae804589ce3a6f1a9ad5e52ed69c279fd3/slides/_revealjs/uni.png -------------------------------------------------------------------------------- /slides/test_type.md: -------------------------------------------------------------------------------- 1 | --- 2 | customTheme: "_revealjs/style-basic" 3 | transition: "slide" 4 | logoImg: "_revealjs/uni.png" 5 | enableMenu: false 6 | enableTitleFooter: false 7 | enableChalkboard: false 8 | highlightTheme: vs2015 9 | autoPlayMedia: true 10 | --- 11 | 12 | ## Testing Types 13 | 14 | --- 15 | 16 | ## Type-Level Programming 17 | 18 | is hard 19 | 20 | - ~~functional~~ logical programming {.fragment .fade-left} 21 | - unsound {.fragment .fade-left} 22 | - performance {.fragment .fade-left} 23 | - missing equality check {.fragment .fade-left} 24 | 25 | --- 26 | 27 | ## type-plus 28 | 29 | `testType` 30 | -------------------------------------------------------------------------------- /slides/tuple_plus.pad_start.md: -------------------------------------------------------------------------------- 1 | --- 2 | customTheme: "_revealjs/style-basic" 3 | transition: "slide" 4 | logoImg: "_revealjs/uni.png" 5 | enableMenu: false 6 | enableTitleFooter: false 7 | enableChalkboard: false 8 | highlightTheme: vs2015 9 | autoPlayMedia: true 10 | --- 11 | 12 | ## TuplePlus.PadStart 13 | 14 | ```ts 15 | 16 | type R = TuplePlus.PadStart<[1, 2, 3], 5, 0> // [0, 0, 1, 2, 3] 17 | 18 | ``` 19 | -------------------------------------------------------------------------------- /type-plus.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "path": "../.github" 8 | } 9 | ], 10 | "settings": { 11 | "markdown.extension.list.toggle.candidate-markers": ["-", "1."], 12 | "markdown.extension.toc.levels": "2..6", 13 | "markdown.extension.toc.omittedFromToc": { 14 | "type-plus/README.md": ["## Table of Contents", "## Installation"] 15 | }, 16 | "markdown.extension.toc.orderedList": true, 17 | "markdown.extension.toc.unorderedList.marker": "-", 18 | "typescript.tsdk": "node_modules/typescript/lib" 19 | } 20 | } 21 | --------------------------------------------------------------------------------