diff --git a/.changeset/add-serialized.md b/.changeset/add-serialized.md deleted file mode 100644 index 55f606119a..0000000000 --- a/.changeset/add-serialized.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"type-plus": minor ---- - -Add `Serialized` and `Deserialized`, which carry a value's type through a string so a two-line wrapper gives `JSON` or flatted a typed round trip. - -`Serialized` tags a format that keeps circular references, such as flatted's, and does not mix with plain JSON either way. diff --git a/.changeset/drop-tersify.md b/.changeset/drop-tersify.md deleted file mode 100644 index 61ae22089b..0000000000 --- a/.changeset/drop-tersify.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": patch ---- - -Remove the unused `tersify` dependency. -Nothing in `type-plus` imports it since the deprecated exports were removed, and it pulled in `acorn`, about 1.3 MB per install. diff --git a/.changeset/esm-only-build.md b/.changeset/esm-only-build.md deleted file mode 100644 index e313cfa311..0000000000 --- a/.changeset/esm-only-build.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"type-plus": major ---- - -Ship ESM only. The CommonJS build in `cjs/` is gone, and so is the `require` export condition. - -Both builds were about 4.5 MB each, so every install, including transitive ones, carried a second copy of the runtime. -`require('type-plus')` still works on Node.js 20.19 and later, and 22.12 and later, which load ES modules through `require()` without a flag. -`engines.node` now declares that floor: `^20.19.0 || >=22.12.0`. diff --git a/.changeset/fix-json-types.md b/.changeset/fix-json-types.md deleted file mode 100644 index 4fe7cc5920..0000000000 --- a/.changeset/fix-json-types.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -"type-plus": major ---- - -Fix the JSON types and add `JsonCompatible`, `IsJson` and `IsNotJson`. - -- `JsonArray` is readonly, so readonly arrays, tuples and `as const` data are accepted as `JsonTypes`. -- `JsonObject` rejects a property holding `undefined` (`{ a: undefined }`). An optional property (`{ a?: number }`) is still accepted. A read from it is `JsonTypes | undefined` under `noUncheckedIndexedAccess`, and `JsonTypes` without it. -- Add `JsonCompatible`: constrain with `T extends JsonCompatible` to accept any JSON-safe value, including an `interface`, which TypeScript never lets satisfy `JsonTypes`. The compiler error names the path of the part that is not JSON, such as `"not JSON at $.profile.createdAt"`. -- Add the `IsJson` and `IsNotJson` predicates, with the usual `selection`, `distributive` and branch options. -- Remove `JsonTypes.get` and the `JsonTypes` and `JSONTypes` values. `type-plus` only ever exported them as types, so they were unreachable. The deprecated `JSONTypes` type alias stays until 9.0. - -Migration: -- Replace `JsonTypes.get(obj, 'a', 'b')` with a plain property read and a cast. -- Replace a `T extends JsonTypes` constraint that has to accept interfaces with `T extends JsonCompatible`. diff --git a/.changeset/optional-typescript-peer-813.md b/.changeset/optional-typescript-peer-813.md deleted file mode 100644 index bbd6b703d1..0000000000 --- a/.changeset/optional-typescript-peer-813.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"type-plus": patch ---- - -Mark the `typescript` peer dependency as optional. - -npm 7 and later install a required peer automatically, so every install of `type-plus`, including transitive ones from packages that only use its runtime functions, pulled in TypeScript 7 and its native compiler binary, about 30 MB. -The `>= 5.4.0` range still applies when TypeScript is installed. diff --git a/.changeset/pre.json b/.changeset/pre.json deleted file mode 100644 index 306ca50d98..0000000000 --- a/.changeset/pre.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mode": "exit", - "tag": "beta" -} diff --git a/.changeset/pre/array-filter-fn-701.md b/.changeset/pre/array-filter-fn-701.md deleted file mode 100644 index 5b000cb159..0000000000 --- a/.changeset/pre/array-filter-fn-701.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'type-plus': patch ---- - -Accept a type function (`$Fn`) in `ArrayPlus.Filter`, and so in `Filter` and `KeepMatch` on an array. - -`Filter, IsObject.$Fn>` returned `never[]`, because `ArrayPlus.Filter` compared the element type against the function with `extends`. -It now keeps the members of the element type that the function returns `true` for (`Array<{ a: 1 }>`), and returns `never[]` when the function matches none of them. diff --git a/.changeset/pre/array-plus-join-747.md b/.changeset/pre/array-plus-join-747.md deleted file mode 100644 index 5b7cc2f678..0000000000 --- a/.changeset/pre/array-plus-join-747.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'type-plus': minor ---- - -Add `ArrayPlus.Join`, the type-level `Array.prototype.join`. It joins a tuple into a string and is the inverse of `StringPlus.Split`. - -```ts -type R = ArrayPlus.Join<['a', 'b', 'c']> // 'a,b,c' -type R = ArrayPlus.Join<['a', 'b', 'c'], '/'> // 'a/b/c' -``` diff --git a/.changeset/pre/assert-type-validator.md b/.changeset/pre/assert-type-validator.md deleted file mode 100644 index 5e4d26e3f5..0000000000 --- a/.changeset/pre/assert-type-validator.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'type-plus': minor ---- - -Add `assertType(subject, validator, message?)`, the throwing counterpart of `isType()` for pre- and post-conditions. -It throws a `TypeError` unless `validator` passes, and narrows `subject` to `T` after the call. - -```ts -assertType(value, (v) => typeof v === 'string', 'value must be a string') -value // string -``` - -The validator is required. Unlike the `assertType` removed earlier in 8.0, it has no no-validator overload and no `isX` members. diff --git a/.changeset/pre/assignability-specials-349.md b/.changeset/pre/assignability-specials-349.md deleted file mode 100644 index 5070d2f97e..0000000000 --- a/.changeset/pre/assignability-specials-349.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -'type-plus': major ---- - -Answer `any`, `unknown`, `never` and `void` with TypeScript's own assignability relation in -`CanAssign`, `IsAssign`, `StrictCanAssign`, `Assignable` and `NotAssignable`. - -`CanAssign` was a bare `A extends B` behind a `boolean` guard. `extends` is not -assignability: it gets all three special types wrong, and `boolean extends A` swallowed `any` -and `unknown` on the way past. The rule now applied everywhere is the compiler's: - -- `any` is assignable to every type except `never`, and every type is assignable to `any`. - The relation is **symmetric** for `any`, exactly as it is in TypeScript — both - `const b: number = a` and `const c: any = n` compile. -- `unknown` is the top type: everything is assignable to it, and it is assignable only to - `any` and `unknown`. -- `never` is the bottom type: it is assignable to everything, and nothing but `never` is - assignable to it. -- `void` is not special to the relation and is answered structurally. -- Unknown-like unions such as `{} | null | undefined`, which TypeScript relates to `unknown` in - both directions, are compared structurally rather than answered as `unknown`. - -Before / after: - -```ts -type R = CanAssign // was false, now true <- #349 -type R = CanAssign // was never, now true -type R = CanAssign // was never, now true -type R = CanAssign // was never, now true -type R = CanAssign // was never, now true - -type R = StrictCanAssign // was true, now false -type R = Assignable // was true, now false -type R = NotAssignable // was false, now true - -type R = Assignable<1, void> // was true, now false -type R = Assignable // was true, now false -type R = NotAssignable // was true, now false -``` - -`Assignable` and `NotAssignable` had no `$void` branch at all, so a `void` on either side fell -through `$Special` to an unconditional `$then`. That is why the two of them, which are supposed to -be complements, both answered `true` for `<1, void>` and both answered `true` for ``. - -Unchanged: distribution over unions (`CanAssign` is still `boolean`), -the `boolean` special case (`CanAssign` is still `false`), the `$any` / `$unknown` -/ `$never` branch overrides, and `Assignable.$` / `IsExtend`, which remain the raw -`A extends B` forms for anyone who wants the unadorned behavior. - -Closes [#349](https://github.com/unional/type-plus/issues/349). diff --git a/.changeset/pre/blue-pumpkins-accept.md b/.changeset/pre/blue-pumpkins-accept.md deleted file mode 100644 index 142c985613..0000000000 --- a/.changeset/pre/blue-pumpkins-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Update `IsSymbol` and variances diff --git a/.changeset/pre/branch-marker-interfaces-684.md b/.changeset/pre/branch-marker-interfaces-684.md deleted file mode 100644 index 995c91ee93..0000000000 --- a/.changeset/pre/branch-marker-interfaces-684.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -'type-plus': major ---- - -Make the branch markers named interfaces, and declare every predicate's `$Options` as an interface. - -`$Then`, `$Else`, `$Any`, `$Unknown`, `$Never`, `$NotNever` and `$Void` were string subtypes built -on `$Type`. They are now interfaces extending `$Branch`, which carries the branch name in a -namespaced string key: - -```ts -export interface $Branch

{ - readonly '~type-plus/branch': P -} -export interface $Then extends $Branch<'$then'> {} -``` - -`$Branch.$Key` names that key (`'~type-plus/branch'`), so type-level code can read a marker's -name with `$B[$Branch.$Key]` without spelling the key out. - -Markers print by name in hovers and errors, are no longer strings, and stay distinct from each other: - -```ts -const a: $Then = 1 -// was: Type '1' is not assignable to type '$<"branch", "$then"> & "$then"'. -// now: Type 'number' is not assignable to type '$Then'. - -type R = IsString<$Then> // was true, now false -``` - -The `$Options` of each predicate (`IsObject.$Options`, `IsNever.$Options`, `Assignable.$Options`, -and 60 more) is now an `interface … extends …` instead of an intersection alias. Expanding one -now shows `$Options` instead of `$Selection.Options & … & $InputOptions<($<"branch", "$any"> & "$any") | …>`, -and each use costs 2 to 4 fewer type instantiations. - -**Breaking** only for code that: - -- reads `_$value` (or `$Type.$ValueKey`) on a marker — read `$Branch.$Key` instead, or match - the marker with `extends`; -- relies on a marker being a string, such as `$Then extends string` or `$Then extends '$then'`. - -Refs [#689](https://github.com/cyberuni/type-plus/issues/689). -Closes [#684](https://github.com/cyberuni/type-plus/issues/684). diff --git a/.changeset/pre/bright-ghosts-count.md b/.changeset/pre/bright-ghosts-count.md deleted file mode 100644 index d07b8b5d28..0000000000 --- a/.changeset/pre/bright-ghosts-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Update `IsUnknown` and `IsNotUnknown`. Remove `UnknownType` and `NotUnknownType`. diff --git a/.changeset/pre/builtin-shadowing-719.md b/.changeset/pre/builtin-shadowing-719.md deleted file mode 100644 index a0e4779047..0000000000 --- a/.changeset/pre/builtin-shadowing-719.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -'type-plus': minor ---- - -Add `ObjectPlus.Partial`, `ObjectPlus.Required`, `ObjectPlus.Pick` and `ObjectPlus.Omit`, which replace -the top-level `Partial`, `Required`, `Pick` and `Omit` (#719). The top-level names are removed in 8.0.0 -(#740). - -The top-level names shadow the TypeScript built-ins of the same name, but they mean something else. -An editor's auto-import can bring one in, and from then on every use of that name in the file -switches meaning without an error: - -- `Required` also strips `undefined` from properties that were already required: - `{ b: string | undefined }` becomes `{ b: string }`. The built-in only removes the `?`. -- `Pick` and `Omit` distribute over a union `T` and accept the keys of any member. The built-ins work - on the keys every member shares, so `Omit<{ k: 'x'; x: 1 } | { k: 'y'; y: 2 }, 'k'>` is - `{ x: 1 } | { y: 2 }` here and `{}` with the built-in. -- `Partial` adds `| undefined` to each property. It differs from the built-in only under - `exactOptionalPropertyTypes`. - -In the `ObjectPlus` namespace they no longer shadow anything. Each type's TSDoc names its -difference from the built-in. - -`Exclude` keeps its top-level name. With two arguments it is identical to the built-in, so shadowing -it changes nothing; `$O['$excluded']` only adds behavior. - -Migration: - -| Before | After | -| --- | --- | -| `import type { Pick } from 'type-plus'` | `import type { ObjectPlus } from 'type-plus'`, then `ObjectPlus.Pick` | -| `Omit` from `type-plus` | `ObjectPlus.Omit` | -| `Partial` from `type-plus` | `ObjectPlus.Partial` | -| `Required` from `type-plus` | `ObjectPlus.Required` | - -Do not just delete the import: the file then compiles against the built-in, which gives different -results in the cases above. diff --git a/.changeset/pre/calm-cats-beam.md b/.changeset/pre/calm-cats-beam.md deleted file mode 100644 index 3bf49168d3..0000000000 --- a/.changeset/pre/calm-cats-beam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Fix `IsPositive` should return `boolean` diff --git a/.changeset/pre/cast-symmetry-750.md b/.changeset/pre/cast-symmetry-750.md deleted file mode 100644 index fd6d7b587b..0000000000 --- a/.changeset/pre/cast-symmetry-750.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'type-plus': patch ---- - -Document that `NumericToString` is the inverse of `StringToNumber`, `StringToBigint` and `StringToNumeric` alike, so the casts need no `NumberToString` or `BigintToString`. -`StringToNumber` and `StringToBigint` now say which strings they reject and point to `StringToNumeric` and `NumericToString`. diff --git a/.changeset/pre/chilly-rules-applaud.md b/.changeset/pre/chilly-rules-applaud.md deleted file mode 100644 index c8c2d6d024..0000000000 --- a/.changeset/pre/chilly-rules-applaud.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Fix `$ResolveOptions` type to handle `undefined` value. diff --git a/.changeset/pre/dirty-stingrays-greet.md b/.changeset/pre/dirty-stingrays-greet.md deleted file mode 100644 index 7a0c6cd1f5..0000000000 --- a/.changeset/pre/dirty-stingrays-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Move source under `packages/type-plus`. diff --git a/.changeset/pre/divide-766.md b/.changeset/pre/divide-766.md deleted file mode 100644 index 2a98ab51ec..0000000000 --- a/.changeset/pre/divide-766.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'type-plus': minor ---- - -Add `Divide`, `A / B` on `number` and `bigint` literals, also available as `MathPlus.Divide`. - -A result that does not terminate is truncated toward zero to `precision` fractional digits, 16 by default: `Divide<1, 3>` is `0.3333333333333333`, the same literal as the runtime `1 / 3`. -Set another precision with `Divide<2, 3, { precision: 2 }>` (`0.66`). -When either input is a `bigint`, `Divide` is `Quotient`, since a `bigint` has no fractional values: `Divide<7n, 2n>` is `3n`. -A zero divisor resolves to `$fail` (`never` by default), as in `Quotient`. diff --git a/.changeset/pre/dull-owls-trade.md b/.changeset/pre/dull-owls-trade.md deleted file mode 100644 index 098ae34398..0000000000 --- a/.changeset/pre/dull-owls-trade.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"type-plus": minor ---- - -Rename `$ExactOptions` to `$Exact.Options`, -`$ExactDefault` to `$Exact.Default`, -`$IsExact` to `$Exact.Parse` diff --git a/.changeset/pre/extract-manipulated-string-name.md b/.changeset/pre/extract-manipulated-string-name.md deleted file mode 100644 index 7c38450f12..0000000000 --- a/.changeset/pre/extract-manipulated-string-name.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'type-plus': major ---- - -Rename `$ExtractManipulatedString` to `ExtractManipulatedString`. - -A leading `$` marks the type-branching machinery (`$Then`, `$Else`, `$Selection`, `X.$Options`). -`ExtractManipulatedString` is a string util that takes no part in it, so the prefix misled. -The type is new in 8.0, so no stable release is affected. - -Migration: replace `$ExtractManipulatedString` with `ExtractManipulatedString`. diff --git a/.changeset/pre/fast-dolls-design.md b/.changeset/pre/fast-dolls-design.md deleted file mode 100644 index 80039e7444..0000000000 --- a/.changeset/pre/fast-dolls-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Replace `Equal` with the new `$Equal` type. diff --git a/.changeset/pre/fast-peaches-occur.md b/.changeset/pre/fast-peaches-occur.md deleted file mode 100644 index 425daef734..0000000000 --- a/.changeset/pre/fast-peaches-occur.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Rename `$IsDistributive` to `$Distributive.Parse`. diff --git a/.changeset/pre/find-last-fn-298.md b/.changeset/pre/find-last-fn-298.md deleted file mode 100644 index f72bab5ab8..0000000000 --- a/.changeset/pre/find-last-fn-298.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -'type-plus': minor ---- - -Accept a type function (`$Fn`) as the criteria of `FindLast` (and `ArrayPlus.FindLast`), as `FindFirst` and `Find` already do. - -With a type function, a union entry is matched one member at a time, and for an array the matching element types come back with `| undefined`: - -```ts -type R = FindLast<[1, 'x', { a: 1 }, 2], IsObject.$Fn> // { a: 1 } -type R = FindLast, IsObject.$Fn> // { a: 1 } | undefined -``` - -This gives `FindLast` the strict mode that every other type taking a `Criteria` now has through `Equal.$Fn`, which matches an entry exactly and does not widen (#298): - -```ts -type R = FindLast<[1, 2, 3], number> // 3 -type R = FindLast<[1, 2, 3], Equal.$Fn> // never -``` diff --git a/.changeset/pre/flat-clouds-reply.md b/.changeset/pre/flat-clouds-reply.md deleted file mode 100644 index 61536631b8..0000000000 --- a/.changeset/pre/flat-clouds-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Add `typescript` peer dependency (v5.6.0 or higher). diff --git a/.changeset/pre/fluffy-files-flash.md b/.changeset/pre/fluffy-files-flash.md deleted file mode 100644 index 143b112106..0000000000 --- a/.changeset/pre/fluffy-files-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Optimize `SplitAt` by moving never check of `DeleteCount` to the top. diff --git a/.changeset/pre/fresh-spiders-invite.md b/.changeset/pre/fresh-spiders-invite.md deleted file mode 100644 index 556f0d3b35..0000000000 --- a/.changeset/pre/fresh-spiders-invite.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": patch ---- - -Removing incorrect usage of the `typesVersions` field in `package.json`. - diff --git a/.changeset/pre/gentle-papayas-beg.md b/.changeset/pre/gentle-papayas-beg.md deleted file mode 100644 index e9b247658c..0000000000 --- a/.changeset/pre/gentle-papayas-beg.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -fix `IsArray` diff --git a/.changeset/pre/getter-merge-598.md b/.changeset/pre/getter-merge-598.md deleted file mode 100644 index 77930d4471..0000000000 --- a/.changeset/pre/getter-merge-598.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'type-plus': patch ---- - -Fix `Merge` and `ObjectPlus.Merge` dropping `readonly` when the two types are disjoint. - -`{ ...a, ...b }` copies property values onto a fresh object, so the result is always -writable. The disjoint fast path returned `A & B` unchanged, which kept `readonly` from -either side. Because a get-only accessor is a `readonly` property, that made a getter -merge in as a read-only property instead of a plain data property: - -```ts -type R = Merge<{ get config(): ResolvedConfig }, { a: string }> -// was: { readonly config: ResolvedConfig } & { a: string } -// now: { config: ResolvedConfig; a: string } -``` - -Closes [#598](https://github.com/unional/type-plus/issues/598). diff --git a/.changeset/pre/good-cups-approve.md b/.changeset/pre/good-cups-approve.md deleted file mode 100644 index 2c05e9b666..0000000000 --- a/.changeset/pre/good-cups-approve.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"type-plus": patch ---- - -Remove `exports.default` in `package.json`. - -That provide the wrong file to systems expecting CJS. -May need to add a different one for browser-spec. diff --git a/.changeset/pre/good-squids-yawn.md b/.changeset/pre/good-squids-yawn.md deleted file mode 100644 index 4d6eb47348..0000000000 --- a/.changeset/pre/good-squids-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -`$Equality` to `$Equal`. diff --git a/.changeset/pre/great-lions-refuse.md b/.changeset/pre/great-lions-refuse.md deleted file mode 100644 index dc22f5c14a..0000000000 --- a/.changeset/pre/great-lions-refuse.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -'type-plus': major ---- - -Remove the types and functions deprecated in v7. - -| Removed | Replacement | -| --- | --- | -| `First` | `FindFirst`, `ArrayPlus.Find` | -| `isType.t()` | `isType()`, `testType.true()` | -| `isType.f()` | `isType()`, `testType.false()` | -| `isType.never()` | `isType()`, `testType.never()` | -| `isType.equal()` | `testType.equal()` | -| `CommonKeys` | `CommonPropKeys` | -| `PadLeft` | `PadStart` | - -`isType()` itself is unchanged — only the `t`, `f`, `never` and `equal` members hanging off it are gone. - -Newly deprecated, to be removed in the next major: - -| Deprecated | Replacement | -| --- | --- | -| `Concat` | `ArrayPlus.Concat` | -| `drop()` | none — the type does not sufficiently cover the use cases | diff --git a/.changeset/pre/grumpy-bananas-jog.md b/.changeset/pre/grumpy-bananas-jog.md deleted file mode 100644 index 948e8228b9..0000000000 --- a/.changeset/pre/grumpy-bananas-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Deprecate `NoInfer`. diff --git a/.changeset/pre/hide-internals-717.md b/.changeset/pre/hide-internals-717.md deleted file mode 100644 index 6d60342834..0000000000 --- a/.changeset/pre/hide-internals-717.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'type-plus': major ---- - -Stop exposing implementation helpers as public namespace members. - -157 helpers were reachable from the package entry, for example `IsNumber._D`, `IsBigint._SD`, -`IsIntegerLiteral._L`, `IsNever._O`, `Pick._`, `Some._Fn`, `$Fn._Test`, `$StrictOptions._Message`, -`ArrayPlus.IndexAt._`, `TuplePlus.Filter._Fn`, and every `X.$UtilOptions`. -The undocumented `Some.Strict`, `Some.StrictArray`, `Some.StrictTuple`, `Some.Loose`, `Some.LooseArray` -and `Some.LooseTuple` were public too. -They are now unexported module-level types, so they no longer show up in autocomplete and can change -without a breaking release. The public types they back behave the same. - -Migration: - -- `X._*` and `Some.Strict*`/`Some.Loose*`: use the public type (`IsNumber`, `IsNumber.$`, - `Some`) instead. -- `X.$UtilOptions`: spell out the options it stood for. For `Assignable` and `NotAssignable` that is - `$Selection.Options & $Distributive.Options`; the predicates that take `exact` add `& $Exact.Options`. -- `$StrictOptions._Message`: read the message off the constraint, - `$StrictOptions<{ [k in K]: unknown }, A>[K]`. diff --git a/.changeset/pre/hungry-badgers-repair.md b/.changeset/pre/hungry-badgers-repair.md deleted file mode 100644 index 557779ec65..0000000000 --- a/.changeset/pre/hungry-badgers-repair.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'type-plus': patch ---- - -Ship a `cjs/package.json` marker so `require('type-plus')` works again. - -The package root declares `"type": "module"`, so Node treated the CommonJS -output in `cjs/` as ESM. `cjs/index.js` then resolved its own `require()` -calls against the caller instead of against the package, and every CJS -consumer failed with `Cannot find module './array/array_plus.js'`. - -The build now writes `cjs/package.json` (`{"type":"commonjs"}`) and, for -symmetry, `esm/package.json` (`{"type":"module"}`) after each compile. The -7.x build emitted the CJS marker via `buddy ts build cjs`; moving to a plain -`tsc` emit dropped it. diff --git a/.changeset/pre/is-equal-options-707.md b/.changeset/pre/is-equal-options-707.md deleted file mode 100644 index 69e29cfa5a..0000000000 --- a/.changeset/pre/is-equal-options-707.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -'type-plus': major ---- - -Merge `Equal` into `IsEqual`, move `IsLiteral` and `Some` onto `$Options`, -and remove the last deprecated positional aliases (#707). - -**Behavior change: `IsEqual` no longer treats symbols as equal to each other.** -The old `IsEqual` fell back to "both are symbols, so equal" whenever the structural check failed. -It now uses `Equal`'s implementation, which has no such fallback: - -| | before | after | -| --- | --- | --- | -| `IsEqual` | `true` | `true` | -| `IsEqual` (two distinct `unique symbol`s) | `true` | `false` | -| `IsEqual` | `true` | `false` | - -`testType.equal` routes through `IsEqual`, so an assertion such as -`testType.equal(true)` now fails to compile and must become `(false)`. - -`IsEqual` and `IsNotEqual` take the `$Options` object in place of positional `Then`/`Else`, -and gain `{ selection: 'filter' }`, `$Branch` and `$Fn`. -`IsNotEqual` keeps `A` when it differs from `B`, -which is the filter form #275 asked for. - -`Some`'s `Mode` moves into `$O` as `mode`, beside `selection` and the branch overrides. - -Migration: - -| Before | After | -| --- | --- | -| `Equal` | `IsEqual` | -| `Equal.$Fn`, `Equal.$Branch`, `Equal.$Same`, ... | `IsEqual.$Fn`, `IsEqual.$Branch`, `IsEqual.$Same`, ... | -| `IsEqual` | `IsEqual` | -| `IsNotEqual` | `IsNotEqual` | -| `NotEqual` | `IsNotEqual` | -| `IsLiteral` | `IsLiteral` | -| `Some` | `Some` | -| `Some` | `Some` | -| `CanAssign`, `IsAssign` | `Assignable` | -| `StrictCanAssign` | `Assignable` | -| `Extendable` | `Assignable.$` | -| `NotExtendable` | `NotAssignable.$` | -| `IsExtend` | `Assignable.$` | -| `IsNotExtend` | `NotAssignable.$` | - -`StringIncludes` and `NotUnknownOr` keep their positional parameters by design. diff --git a/.changeset/pre/is-not-any-or-never-749.md b/.changeset/pre/is-not-any-or-never-749.md deleted file mode 100644 index 125daf0028..0000000000 --- a/.changeset/pre/is-not-any-or-never-749.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'type-plus': minor ---- - -Add `IsNotAnyOrNever`, the negation of `IsAnyOrNever` (#749): - -```ts -type R = IsNotAnyOrNever // false -type R = IsNotAnyOrNever // false -type R = IsNotAnyOrNever<1> // true -``` diff --git a/.changeset/pre/itchy-cups-complain.md b/.changeset/pre/itchy-cups-complain.md deleted file mode 100644 index b542bed5ae..0000000000 --- a/.changeset/pre/itchy-cups-complain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Remove `$Override`. It is not needed. diff --git a/.changeset/pre/itchy-mails-sleep.md b/.changeset/pre/itchy-mails-sleep.md deleted file mode 100644 index dead8e484a..0000000000 --- a/.changeset/pre/itchy-mails-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Simplify `Omit` type as the simpler code is working with typescript 5.4 diff --git a/.changeset/pre/itchy-planes-remember.md b/.changeset/pre/itchy-planes-remember.md deleted file mode 100644 index 5815af9048..0000000000 --- a/.changeset/pre/itchy-planes-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Update bit types diff --git a/.changeset/pre/json-title-case.md b/.changeset/pre/json-title-case.md deleted file mode 100644 index 9cd45da84c..0000000000 --- a/.changeset/pre/json-title-case.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'type-plus': minor ---- - -Rename `JSONTypes`, `JSONPrimitive`, `JSONObject` and `JSONArray` to `JsonTypes`, `JsonPrimitive`, `JsonObject` and `JsonArray`. - -`type-plus` title-cases acronyms (`IsBigint`, `StringToBigint`), and the JSON types were the exception. -The old names remain as deprecated aliases of the same types and will be removed in 9.0. - -Migration: replace `JSONTypes` with `JsonTypes`, and so on for the other three. diff --git a/.changeset/pre/khaki-queens-lay.md b/.changeset/pre/khaki-queens-lay.md deleted file mode 100644 index 420a0f7cc9..0000000000 --- a/.changeset/pre/khaki-queens-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Consolidate `$Selection` types. diff --git a/.changeset/pre/khaki-yaks-unite.md b/.changeset/pre/khaki-yaks-unite.md deleted file mode 100644 index a91b64d06b..0000000000 --- a/.changeset/pre/khaki-yaks-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Deprecate `Failed` and `FailedT`. diff --git a/.changeset/pre/large-windows-obey.md b/.changeset/pre/large-windows-obey.md deleted file mode 100644 index d1517ed0de..0000000000 --- a/.changeset/pre/large-windows-obey.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"type-plus": minor ---- - -Update `IsFunction`, `IsNotFunction`, `IsStrictFunction`, `IsNotStrictFunction`. - -Remove `FunctionType`, `NotFunctionType`, `StrictFunctionType`, `NotStrictFunctionType`. diff --git a/.changeset/pre/late-seals-lick.md b/.changeset/pre/late-seals-lick.md deleted file mode 100644 index cbd4572f2b..0000000000 --- a/.changeset/pre/late-seals-lick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Work around excessive stack depth error in TS 5.4 for type `Zeros`. diff --git a/.changeset/pre/left-join-modifiers-205.md b/.changeset/pre/left-join-modifiers-205.md deleted file mode 100644 index eebb26eb1e..0000000000 --- a/.changeset/pre/left-join-modifiers-205.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'type-plus': patch ---- - -Keep the `?` and `readonly` modifiers of `A` in `LeftJoin`. - -`LeftJoin<{ a?: number; b?: string }, { b: boolean }>` returned `{ a: number; b: boolean }`: the properties carried over from `A` lost their modifiers. -It now returns `{ a?: number; b: boolean }`, and a `readonly` property of `A` stays `readonly`. -On a key that both `A` and `B` declare, `B` still wins the type and the modifiers. diff --git a/.changeset/pre/lemon-crabs-vanish.md b/.changeset/pre/lemon-crabs-vanish.md deleted file mode 100644 index bd7ff4489d..0000000000 --- a/.changeset/pre/lemon-crabs-vanish.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'type-plus': patch ---- - -Remove the unreferenced half of the legacy `src/**/readme.md` documentation -tree, and the stale root `info.md`. - -These are v6/v7-era pages documenting an API that no longer exists — for -example `src/numeric/readme.md` still described `Positive`, `Negative`, -`Integer`, `IsWhole` and a `NumericType` signature, none -of which the package exports any more. `src` is listed in `files`, so they were -shipping in the tarball; 13 of the 33 are gone from it now. - -Nothing was lost. The explanation that only lived in these pages was ported to -the documentation site first: branch-option composition, `$Special`, `$Error` / -`$InferError` and the `$Type` brand moved onto the *type branching* page, and -`Equal` — previously undocumented on the site — gained an *Equality* page -carrying the one-level intersection caveat. - -The remaining 20 pages stay for now: `packages/type-plus/readme.md` links into -them 122 times, so they cannot be removed without rewriting the published -readme. diff --git a/.changeset/pre/little-donkeys-pay.md b/.changeset/pre/little-donkeys-pay.md deleted file mode 100644 index dbb09fa3b2..0000000000 --- a/.changeset/pre/little-donkeys-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Update to require typescript 5.4. diff --git a/.changeset/pre/loose-array-type-330.md b/.changeset/pre/loose-array-type-330.md deleted file mode 100644 index ac93448320..0000000000 --- a/.changeset/pre/loose-array-type-330.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -'type-plus': major ---- - -Remove `LooseArrayType`, `IsLooseArray`, `NotLooseArrayType` and `IsNotLooseArray`. - -These were a stopgap added while `ArrayType` still did a strict, tuple-excluding -check (#330). `ArrayType` and its variances are gone in 8.0.0, and their -replacement `IsArray` is loose by default — a tuple is an array, the same way a -string literal is a `string` for `IsString`. That leaves the stopgap with nothing -to stop. - -Migration: - -| Removed | Replacement | -| --- | --- | -| `LooseArrayType` | `IsArray` | -| `IsLooseArray` | `IsArray` | -| `NotLooseArrayType` | `IsNotArray` | -| `IsNotLooseArray` | `IsNotArray` | - -The filter form is not a literal drop-in: `IsArray` distributes over unions, so -`IsArray` is `number[]`, where -`LooseArrayType` returned the whole `number[] | 1`. - -For anyone who relied on the old strict `ArrayType` behaviour, that is -`IsArray` (`{ selection: 'filter', exact: true }` to filter). - -`ArrayPlus.IsReadonly` is now built on `IsArray`; its behaviour is unchanged. diff --git a/.changeset/pre/loud-rocks-create.md b/.changeset/pre/loud-rocks-create.md deleted file mode 100644 index ae7c917ce4..0000000000 --- a/.changeset/pre/loud-rocks-create.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Update `AdjustExactOptionalProps` to trim empty object (`{}`) when the input type has only optional or required properties. diff --git a/.changeset/pre/lovely-horses-press.md b/.changeset/pre/lovely-horses-press.md deleted file mode 100644 index 1a96695765..0000000000 --- a/.changeset/pre/lovely-horses-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Add `IsStrictObject` type. diff --git a/.changeset/pre/lucky-apes-defer.md b/.changeset/pre/lucky-apes-defer.md deleted file mode 100644 index 24497fa69b..0000000000 --- a/.changeset/pre/lucky-apes-defer.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -'type-plus': minor ---- - -Add `testType.defer` and `testType.assert` for deferred type testing. - -A `testType.*` check asserts immediately: the expectation is an argument, so -the failure is reported where the check is written. That makes a check -impossible to extract into a reusable helper — the helper body is checked once, -against type parameters that are not yet resolved, so nothing there can pass or -fail. - -`testType.defer.*` takes no argument and returns the result as a type instead. -A helper returns the results it collected, and `testType.assert()` checks them -at the call site, where the type parameters are resolved: - -```ts -function testMyType() { - return [testType.defer.string(), testType.defer.not.never()] -} - -it('blah', () => { testType.assert(testMyType<'a'>()) }) // passes -it('bruh', () => { testType.assert(testMyType<1>()) }) // fails here -``` - -`testType.assert()` accepts results in any shape a helper returns — a single -result, an array, an object, or any nesting of those. `testType.defer.not.*` is -the deferred form of passing `false`. Deferred checks take the same options as -their immediate counterparts, in the same position and merged over the same -defaults. Every check has a deferred form except `inspect`, which is a -development aid rather than a check. - -A failing check resolves to `testType.Failed` rather -than a bare `false`, so the compiler error names the check that failed and the -types it compared. diff --git a/.changeset/pre/lucky-donkeys-pin.md b/.changeset/pre/lucky-donkeys-pin.md deleted file mode 100644 index fd3981c36c..0000000000 --- a/.changeset/pre/lucky-donkeys-pin.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -'type-plus': patch ---- - -Correct the TSDoc for the `string`, `object`, `tuple` and `union` types, and pin every documented -example to the implementation. - -Twenty-four documented claims did not match what the types actually resolve to: - -- `IsNotString` and `IsNotString` were documented as `false`; the special types are - not strings, so the negation accepts them and both are `true`. `IsNotString` was documented as `false` and is `true`. -- `IsStringLiteral` and `IsNotStringLiteral` illustrated the `exact` option with `'${number}'`, an - ordinary string literal, where the template literal `` `${number}` `` was meant. -- `IsObject`'s first example asserted `IsNotObject // true` — the wrong type, and the wrong - answer for it. The `{ selection: 'filter' }` examples for `IsObject`, `IsNotObject` and - `IsNotTuple` were missing the option they were demonstrating. -- `DropLast<[1, 2, 3]>` was documented as `[2, 3]`, copied from `DropFirst`; it is `[1, 2]`. -- `IsNotTuple<[] | 1, { distributive: false }>` was documented as `false` and is `true`. -- `TuplePlus.Find<[true, number | string], string>` was documented as `string | undefined`; - `$unionNotMatch` defaults to `never`, so it is `string`. The example now shows both. -- `TuplePlus.Filter`'s examples were written as bare `Filter`, which resolves to the unrelated - array `Filter`, and `UnionType`'s examples were written as `IsUnion`. -- The branch examples used names that do not exist — `$IsString.$Branch`, `$IsNotStringLiteral.$Branch` - and the retired `$SelectionBranch` — and two of them named `IsString` in `IsTemplateLiteral`'s docs. - -On the docs site, `$ExtractManipulatedString>` was documented as `'abc'`. The -intrinsic resolves before the type sees it, so the result is `'ABC'`; only an unresolved intrinsic -can be seen through. - -`src/string/string_docs.spec.ts`, `src/object/object_docs.spec.ts`, `src/tuple/tuple_docs.spec.ts` -and `src/union/union_docs.spec.ts` now pin every documented example with `testType.equal`, so an -example that drifts from the implementation fails to compile. diff --git a/.changeset/pre/lucky-socks-walk.md b/.changeset/pre/lucky-socks-walk.md deleted file mode 100644 index 0ecb626856..0000000000 --- a/.changeset/pre/lucky-socks-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Rename `case*` to `$*` to make them easier to use. diff --git a/.changeset/pre/math-comparisons-745.md b/.changeset/pre/math-comparisons-745.md deleted file mode 100644 index 478cc30ef1..0000000000 --- a/.changeset/pre/math-comparisons-745.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'type-plus': minor ---- - -Add `LessThan`, `GreaterThanOrEqual`, `LessThanOrEqual` and `Min`, and the matching `MathPlus` members. - -They compare `number` literals and share the limits of `GreaterThan`: `bigint`, the wide `number`, and a fractional pair whose difference is a whole number resolve to `$fail` (`never` by default). -`GreaterThanOrEqual` and `LessThanOrEqual` return that `$fail` value as is, not negated, and are `true` for two identical `number` literals, including fractional ones such as `GreaterThanOrEqual<1.5, 1.5>`. - -There is no `MathPlus.ToPositive`: `Abs` is that type. diff --git a/.changeset/pre/metal-buckets-stare.md b/.changeset/pre/metal-buckets-stare.md deleted file mode 100644 index 668b2c4caa..0000000000 --- a/.changeset/pre/metal-buckets-stare.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"type-plus": patch ---- - -Add docs for `ArrayPlus.IsReadonly`. - -The following are internal changes thus not considered a breaking change: - -- Replace `MergeOptions`/`MergeCases` with `TypePlusOptions.Merge`. -- Rename `TypePlusOptions.Predicate` to `TypePlusOptions.Selection`. diff --git a/.changeset/pre/mighty-pumas-wink.md b/.changeset/pre/mighty-pumas-wink.md deleted file mode 100644 index 9f6927203f..0000000000 --- a/.changeset/pre/mighty-pumas-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Update `$Type` to work with primitive types and improve object type usage. diff --git a/.changeset/pre/moody-badgers-refuse.md b/.changeset/pre/moody-badgers-refuse.md deleted file mode 100644 index 8545f3a33e..0000000000 --- a/.changeset/pre/moody-badgers-refuse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Remove `StrictBigintType` and `NotStrictBigintType` diff --git a/.changeset/pre/namespace-surface-716.md b/.changeset/pre/namespace-surface-716.md deleted file mode 100644 index 40747d8c5a..0000000000 --- a/.changeset/pre/namespace-surface-716.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'type-plus': minor ---- - -Tidy the namespace surface. Nothing is removed; the losing names are deprecated and will go in a -later major. - -- `Bit` is the one name for the bit namespace. `B` is deprecated: it is the name type parameters are - most often given (`And`), so a type parameter `B` shadows the namespace inside any generic that - declares one. Replace `B.And` with `Bit.And`. -- `NumberPlus` is deprecated in favor of `NumericPlus`. It held the same members plus `IsNumber` and - `IsNotNumber`, which are on the top level. Replace `NumberPlus.IsInteger` with - `NumericPlus.IsInteger`, and `NumberPlus.IsNumber` with `IsNumber`. -- `MathPlus` now holds every math type: `Abs`, `GreaterThan` and `Max` join `Add`, `Decrement`, - `Increment`, `Multiply`, `Subtract` and `ToNegative`. -- `KeepMatch`, `PropUnion` and `MapToProp` are deprecated. Use `Filter`, `UnionOfProps` and - `IntersectOfProps`, the same types under their primary names. -- The `ArrayPlus` and `TuplePlus` TSDoc now lists what each namespace exports. diff --git a/.changeset/pre/namespace-tsdoc.md b/.changeset/pre/namespace-tsdoc.md deleted file mode 100644 index 01ead261da..0000000000 --- a/.changeset/pre/namespace-tsdoc.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'type-plus': major ---- - -Make the `ArrayPlus`, `MathPlus`, `NumericPlus`, `ObjectPlus`, `StringPlus`, `TuplePlus` and `Bit` namespaces show their TSDoc on hover. - -Six of them were `export * as X` re-exports of a module. TypeScript never attaches a doc comment to such a namespace, so hovering `ArrayPlus` showed nothing. All seven are now declared namespaces whose members alias the types they group. The members are the same types as before. - -**Breaking, for `verbatimModuleSyntax` only.** A declared namespace exists only at the type level, so the package no longer exports an (empty) runtime object for `ArrayPlus`, `MathPlus`, `NumericPlus`, `ObjectPlus` or `TuplePlus`. Under `verbatimModuleSyntax`, a value import of one now fails with TS1484. `StringPlus` and `Bit` already worked this way. - -Migration: import them as types. - -```ts -import type { ArrayPlus } from 'type-plus' -// or -import { type ArrayPlus, testType } from 'type-plus' -``` diff --git a/.changeset/pre/negated-predicate-fast-path.md b/.changeset/pre/negated-predicate-fast-path.md deleted file mode 100644 index ee5eca0b3e..0000000000 --- a/.changeset/pre/negated-predicate-fast-path.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'type-plus': patch ---- - -`IsNotAny`, `IsNotNever`, `IsNotUnknown`, `IsNotVoid`, `IsNotObject`, `IsNotString` and -`NotAssignable` skip the options machinery when called without options, like their positive -counterparts. Results are unchanged. - -Instantiations per use without options (TypeScript 6.0 and 7, 300 distinct inputs): - -| Type | Before | After | -| --- | ---: | ---: | -| `IsNotAny` | 29 | 10 | -| `IsNotNever` | 29 | 13 | -| `IsNotUnknown` | 29 | 12 | -| `IsNotVoid` | 68 | 23 | -| `IsNotObject` | 61 | 25 | -| `IsNotString` | 59 | 24 | -| `NotAssignable` | 67 | 39 | - -"Before" already includes the cheaper `$Special` guards from the same release. On 8.0.0-beta.11 each -type cost 1.5 to 2 times its "Before" figure. diff --git a/.changeset/pre/negations-715.md b/.changeset/pre/negations-715.md deleted file mode 100644 index 41c63d25e0..0000000000 --- a/.changeset/pre/negations-715.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'type-plus': minor ---- - -Add the missing negations of the predicates: -`IsNotUnion`, `IsNotLiteral`, `IsNotOptionalKey`, `IsNotEmptyObject`, `IsNotRecord`, -`HasNoKey`, `HasNoNull`, `HasNoUndefined` and `HasNoVoid`. - -Each one inverts its positive form, keeps the same rules, and has the same `$Options`, `$Default`, `$Branch` and `.$Fn` surface -(`IsNotLiteral` has no `.$Fn`, because `IsLiteral` has none). -The filter form keeps the input that fails the positive check: - -```ts -type R = IsNotUnion // number -type R = HasNoNull<1 | 2, { selection: 'filter' }> // 1 | 2 -type R = HasNoKey<{ a: 1; b: 2 }, 'a' | 'c', { selection: 'filter' }> // 'c' -``` - -For a predicate without a counterpart, such as `IsDisjoint`, negate its type function with `$Fn.Not`. - -This change is not breaking: it only adds exports. diff --git a/.changeset/pre/nice-forks-hide.md b/.changeset/pre/nice-forks-hide.md deleted file mode 100644 index 96d0f07a43..0000000000 --- a/.changeset/pre/nice-forks-hide.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"type-plus": minor ---- - -Accept `readonly` for array. -Add `ArrayPlus.IsReadonly`. - -Fix `ArrayPlus.Reverse` to support `readonly`. diff --git a/.changeset/pre/nine-jars-smile.md b/.changeset/pre/nine-jars-smile.md deleted file mode 100644 index 42901628cc..0000000000 --- a/.changeset/pre/nine-jars-smile.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'type-plus': patch ---- - -Document the 8 undocumented exports in `src/predicates/` and the 7 branch -markers in `src/$type/`. - -Every `@example` is pinned by a compiled assertion in -`src/predicates/predicates_docs.spec.ts` and `src/$type/$type_docs.spec.ts`. - -`$Then`, `$Else`, `$Selection`, `$Distributive` and `$Exact` are what the -`$Options`/`$Branch` convention rests on, so they now say what a caller passes -and what comes back. - -Six of the predicates are listed in #665 for removal or migration. Each says so -in its own TSDoc rather than reading as current API. diff --git a/.changeset/pre/ninety-dolphins-refuse.md b/.changeset/pre/ninety-dolphins-refuse.md deleted file mode 100644 index 75f6020073..0000000000 --- a/.changeset/pre/ninety-dolphins-refuse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Rename `$SpecialType` to `$Special`. diff --git a/.changeset/pre/numeric-exact-literals-695.md b/.changeset/pre/numeric-exact-literals-695.md deleted file mode 100644 index 5111b3b6a5..0000000000 --- a/.changeset/pre/numeric-exact-literals-695.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -'type-plus': major ---- - -Honor `exact` on the numeric sign and integer predicates, and add the literal variants. - -`IsPositive`, `IsNegative`, `IsInteger`, `IsNotPositive`, `IsNotNegative` and `IsNotInteger` -declared `exact` and never read it. The option compiled and did nothing. It now follows the string -family, where `exact` separates the wide type from its literals: with `exact: true` only the wide -`number` and `bigint` match, and every literal answers the `$else` branch. The sign of a wide type -is unknown, so it stays `boolean`. - -```ts -type R = IsPositive // boolean -type R = IsPositive<1, { exact: true }> // was true, now false -type R = IsInteger // true -type R = IsNotInteger<1, { exact: true }> // was false, now true -``` - -The other half of the split is new: `IsPositiveLiteral`, `IsNegativeLiteral`, `IsIntegerLiteral`, -`IsNotPositiveLiteral`, `IsNotNegativeLiteral` and `IsNotIntegerLiteral` match only literals, the -way `IsNumberLiteral` does for `IsNumber`. - -```ts -type R = IsPositiveLiteral<1> // true -type R = IsPositiveLiteral<1n> // true -type R = IsPositiveLiteral<-1> // false -type R = IsPositiveLiteral // false -type R = IsIntegerLiteral<1.1> // false -``` - -They do not take `exact`. On strings `exact` excludes template literal types such as `${number}`; -numbers have no equivalent middle case — a numeric enum member and a literal intersected with a -record are both literals either way — so the option would compile and do nothing. - -For the same reason `exact` is **removed** from `IsNumberLiteral`, `IsNotNumberLiteral`, -`IsBigintLiteral`, `IsNotBigintLiteral` and `IsNumeric`, none of which ever read it. - -**Breaking** for code that: - -- passes `{ exact: true }` to `IsPositive`, `IsNegative`, `IsInteger`, `IsNotPositive`, - `IsNotNegative` or `IsNotInteger` — the option used to be ignored and now changes the answer. - Drop it to keep the old result, or reach for the matching `*Literal` type; -- passes `exact` to `IsNumberLiteral`, `IsNotNumberLiteral`, `IsBigintLiteral`, - `IsNotBigintLiteral` or `IsNumeric` — with `$StrictOptions` that is now a compile error. Drop it; - the result is unchanged. - -Calls that pass no `exact` answer exactly as before. - -Closes [#695](https://github.com/cyberuni/type-plus/issues/695). diff --git a/.changeset/pre/numeric-special-branches.md b/.changeset/pre/numeric-special-branches.md deleted file mode 100644 index b4b47171db..0000000000 --- a/.changeset/pre/numeric-special-branches.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'type-plus': patch ---- - -Honor the special-type branch options in `IsPositive`, `IsNegative`, `IsInteger`, `IsNotPositive`, `IsNotNegative` and `IsNotInteger`. - -They declared `$any`, `$unknown`, `$never` and `$void` but ignored them. Passing one now overrides that branch: - -```ts -type R = IsPositive // was false, now 1 -type R = IsNotInteger // was true, now 3 -``` - -Without these options the results are unchanged. diff --git a/.changeset/pre/numeric-struct-maybe-303.md b/.changeset/pre/numeric-struct-maybe-303.md deleted file mode 100644 index 5eb7348df3..0000000000 --- a/.changeset/pre/numeric-struct-maybe-303.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'type-plus': patch ---- - -`Add`, `Subtract` and `Multiply` now pass a failed input through the internal -`NumericStruct` operations instead of unwrapping each input first (#303). The -results are unchanged, including every `Fail` result and the results for union -inputs. diff --git a/.changeset/pre/odd-panthers-jog.md b/.changeset/pre/odd-panthers-jog.md deleted file mode 100644 index a1af609bd0..0000000000 --- a/.changeset/pre/odd-panthers-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Add `$Void` support. diff --git a/.changeset/pre/olive-jaguars-repeat.md b/.changeset/pre/olive-jaguars-repeat.md deleted file mode 100644 index 3df6a1b50a..0000000000 --- a/.changeset/pre/olive-jaguars-repeat.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -'type-plus': major ---- - -`ArrayPlus.IndexAt` now takes an options object instead of positional fallback type parameters. - -The three positional parameters `Fail`, `Upper`, and `Lower` are replaced by -`Options`, which covers five cases: - -| Option | Applies when | Default | -| --- | --- | --- | -| `$never` | `A` is `never` | `never` | -| `$array` | `A` is an array (not a tuple) | `N` | -| `caseEmptyTuple` | `A` is `[]` | `never` | -| `caseUpperBound` | `N` is past the upper bound | `A['length']` | -| `caseLowerBound` | `N` is past the lower bound | `0` | - -The `$never` and `$array` cases are new: they were previously not customizable. -Only the cases you specify are overridden; the rest keep their defaults. - -Migration: - -```ts -// before -type R = IndexAt<[1], 1, 'f', 'u', 'l'> -// after -type R = IndexAt<[1], 1, { caseEmptyTuple: 'f'; caseUpperBound: 'u'; caseLowerBound: 'l' }> -``` - -Note that the old `Fail` parameter also covered the `A is never` case. -If you relied on that, set `$never` to the same type as `caseEmptyTuple`. - -Default behavior is unchanged, so `IndexAt` and `ArrayPlus.At` / -`ArrayPlus.IsIndexOutOfBound` are not affected. diff --git a/.changeset/pre/olive-jars-repeat.md b/.changeset/pre/olive-jars-repeat.md deleted file mode 100644 index 11b45afb59..0000000000 --- a/.changeset/pre/olive-jars-repeat.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'type-plus': patch ---- - -Document the 9 undocumented exports in `src/math/`. - -Every `@example` is pinned by a compiled assertion in -`src/math/math_docs.spec.ts`, so an example that drifts from the -implementation fails to build. - -The edges these types were missing documentation for are the ones a caller -cannot guess: - -- `GreaterThan` and `Max` accept `bigint` in their constraint but resolve to - `Fail` for every `bigint` argument. -- Arithmetic on fractional literals whose result is a whole number resolves to - an error string rather than a number. -- `Add`, `Subtract` and `Multiply` are exact decimal, so `Add<0.1, 0.2>` is - `0.3` where the runtime gives `0.30000000000000004`. -- Nothing guards overflow past `Number.MAX_SAFE_INTEGER`. diff --git a/.changeset/pre/olive-moons-decide.md b/.changeset/pre/olive-moons-decide.md deleted file mode 100644 index 209094f924..0000000000 --- a/.changeset/pre/olive-moons-decide.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -'type-plus': minor ---- - -Deprecate `NotExtendable`, `IsExtend` and `IsNotExtend`, completing the group -`Extendable` already belonged to. Nothing is removed and no behavior changes; -each type now names its replacement. - -| Deprecated | Use instead | -| --- | --- | -| `IsExtend` | `Assignable.$` | -| `IsNotExtend` | `NotAssignable.$` | -| `Extendable` | `Assignable.$` | -| `NotExtendable` | `NotAssignable.$` | - -`#665` names the replacement as `$Assignable`, which does not exist. The `$` -type util hanging off `Assignable` and `NotAssignable` is the equivalent, and -the mapping above is pinned by compiled assertions in -`src/predicates/predicates_docs.spec.ts` rather than asserted. - -⚠️ Migrating to plain `Assignable`/`NotAssignable` instead of the `$` member is -**not** a rename. The plain types special-case `any`, `never` and `unknown`, so -three inputs change answer: - -```ts -IsExtend // boolean -Assignable // true -Assignable.$ // boolean -- the equivalent -``` - -`Assignable.$` also requires its options argument explicitly (`{}` at minimum), -and supports `{ distributive: false }`, which the deprecated four cannot -express. diff --git a/.changeset/pre/olive-pots-repeat.md b/.changeset/pre/olive-pots-repeat.md deleted file mode 100644 index 0a3c61d0e9..0000000000 --- a/.changeset/pre/olive-pots-repeat.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'type-plus': minor ---- - -Support TypeScript 5.4 and up. - -The `typescript` peer range widens from `>= 5.6.0` to `>= 5.4.0`. Nothing in -the published types ever required 5.6 — the range was simply narrower than -what the compiler matrix tested. `verify:dts` now type-checks the emitted -`esm/index.d.ts` with 5.4, 5.5, 5.6 and 6.0, so the range is checked rather -than asserted. - -The package is also now built with TypeScript 7 — one build, as before. -Emitted JavaScript is unchanged; the declarations differ only in how the -compiler renders them. diff --git a/.changeset/pre/pink-hoops-classify.md b/.changeset/pre/pink-hoops-classify.md deleted file mode 100644 index 2fcaecb41b..0000000000 --- a/.changeset/pre/pink-hoops-classify.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -'type-plus': patch ---- - -Classify an intersection of a primitive with a record by its primitive constituent, fixing #429. - -`IsStringLiteral`, `IsTemplateLiteral`, and their negations read the kind of a string type through -`` `${T}` ``, and `IsNegative`, `IsPositive`, `IsNotNegative` and `IsNotPositive` read a sign off the -same template. TypeScript 5.1 stopped reducing an intersection inside a template -([microsoft/TypeScript#57918](https://github.com/microsoft/TypeScript/issues/57918), still open), so -`` `${'abc' & { a: 1 }}` `` stayed unresolved and every one of those types answered as though the -string or number were not there: - -```ts -type R = IsStringLiteral<'abc' & { a: 1 }, { exact: true }> // was false, now true -type R = IsTemplateLiteral<'abc' & { a: 1 }> // was true, now false -type R = IsNegative<-1 & { a: 1 }> // was false, now true -``` - -The intersected members are now peeled off before the template is built, using TypeScript's -inference to an intersection target (`T extends infer U & O`) rather than the template reduction TS -withdrew. Both spellings of the members are handled — one record holding several -(`'abc' & { a: 1, b: 2 }`) and several records (`'abc' & { a: 1 } & { b: 2 }`). - -Two shapes remain unreduced, both for the same upstream reason: an intersected index signature -(`'abc' & Record`), whose key is one `String` already declares, and an intersection -nested inside a string manipulation type (`Uppercase<'' & { a: 1 }>`), which never surfaces as a -constituent of the outer type. diff --git a/.changeset/pre/plain-jokes-repeat.md b/.changeset/pre/plain-jokes-repeat.md deleted file mode 100644 index 032f419a70..0000000000 --- a/.changeset/pre/plain-jokes-repeat.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -'type-plus': patch ---- - -Document the last 30 exports with no TSDoc, and give every family a page on the -site. - -Closes #669. `pnpm docs:llms --undocumented` now reports `0 of 273`. - -The 30 fall into two kinds. Eight are the `*Plus` namespace re-exports — -`ArrayPlus`, `NumberPlus`, `NumericPlus`, `TuplePlus`, `StringPlus` and the -`B` / `Bit` pair — which are containers, so each gets one comment saying what -it groups, how it differs from the same names on the top-level surface, and -that its members carry their own docs. The other 22 are ordinary API and get -prose plus a compiled example: the JSON types, `nominalMatch`, `isPromise`, -`mapSeries`, `DropNull` / `DropNullable` / `DropUndefined`, `ToTuple`, -`as` / `asAny`, `TypePlusOptions`, `AnyConstructor`, `AnyFunction`, `ChainFn`, -`ContextBaseShape`, `ContextBuilder`, `SystemErrors` and `SystemErrorCodes`. - -The five families with no page on the site are resolved. `src/binary/` folds -into Boolean and Logical, next to the boolean operators it mirrors, and -`src/class/` folds into Function and Functional, since a class is a constructor -function. `src/nodejs/`, `src/utils/` and the root files get pages of their own: -Node.js, Utilities, and Type Sets and JSON. `llms.txt` no longer carries an -"Undocumented families" bullet. - -Checking those examples against the compiler turned up thirteen documented -claims the implementation disagrees with, corrected here rather than shipped: - -- `FindFirst<[true, number | string], string>` is `string`, not - `string | undefined`; `FindFirst, number>` is `1 | 2`, not - `1 | 2 | undefined`; `FindFirst, number>` is `number`, - not `number | undefined`. Same three for `ArrayPlus.Find`. -- The root cause of those six: `FindFirst` documented its options as - `$noMatch` and `$unionMiss` defaulting to `undefined`. The real names are - `$notMatch` and `$unionNotMatch`, and both default to `never`. -- `KeepMatch<[1, 2, 3], number>` is `[1, 2, 3]`. Nothing is dropped, all three - are numbers. -- `FindLast`'s two examples were written as `ArrayPlus.Find<...>`, a different - type that rejects tuples. The claimed results were right; the symbol was not. -- `IsNotFunction` is `true`, not - `false`, and its `$Branch` example had `$Then` and `$Else` the wrong way - round — `Function` is a function, so `IsNotFunction` takes the else branch. -- `ExtractFunction`'s example was not valid TypeScript. It is - `ExtractFunction<(() => void) & { a: 1 }>`. -- `EitherOrBoth`'s own example called it by its deprecated name, `EitherAnd`. - -`IsFunction` and `IsNotFunction` also still referred to `$SelectionBranch`, -retired in v8, in place of their own `$Branch`. Twenty-four other files still -do; they belong to families outside this change. - -Two generator fixes back the count. `export * as X from './x.js'` aliases a -*module* symbol, whose file-level doc comment TypeScript does not surface -through `getDocumentationComment`, so eight namespaces were reported -undocumented no matter what was written on them; the generator now reads that -comment. And the coverage sentence used to say "the rest have a name and a -signature only" when the rest were the `unpartial` re-exports, documented in -that package. diff --git a/.changeset/pre/polite-games-guess.md b/.changeset/pre/polite-games-guess.md deleted file mode 100644 index f7868a9464..0000000000 --- a/.changeset/pre/polite-games-guess.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"type-plus": patch ---- - -Use `fn.apply` instead of spread. -It has better performance and function that are already bound continue to work as expected. - -```ts -const bindFn = fn.bind(This) - -bindFn.apply(null, args) // `this` is not affected -``` diff --git a/.changeset/pre/predicate-fast-path-683.md b/.changeset/pre/predicate-fast-path-683.md deleted file mode 100644 index c1d0b95482..0000000000 --- a/.changeset/pre/predicate-fast-path-683.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -'type-plus': patch ---- - -Cut the type instantiations spent on special types. Results are unchanged. - -`$Special` detects `any`, `unknown`, `never` and `void` with cheaper checks, which saves about 28 -instantiations per use in every type built on it. `$MergeOptions` returns the defaults directly when -there are no options to merge. - -`IsAny`, `IsNever`, `IsUnknown`, `IsVoid`, `IsObject`, `IsString` and `Assignable` also skip the -options machinery when called without options. The four special-type predicates check `T` -directly. The other three answer through the new `$Special.Values`, which picks an answer by special -type the same way `$Special` does. - -Instantiations per use without options (TypeScript 6.0 and 7, 300 distinct inputs): - -| Type | Before | After | -| --- | ---: | ---: | -| `IsAny` | 52 | 10 | -| `IsNever` | 53 | 13 | -| `IsUnknown` | 52 | 12 | -| `IsVoid` | 102 | 23 | -| `IsObject` | 101 | 25 | -| `IsString` | 97 | 24 | -| `Assignable` | 123 | 39 | diff --git a/.changeset/pre/predicate-fast-path-741.md b/.changeset/pre/predicate-fast-path-741.md deleted file mode 100644 index d930cfa625..0000000000 --- a/.changeset/pre/predicate-fast-path-741.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -'type-plus': patch ---- - -The remaining predicates built on `$Special` skip the options machinery when called without options, -like `IsString` and `IsObject`. They answer through `$Special.Values`. Results are unchanged. - -This covers the `array`, `bigint`, `boolean`, `function`, `null`, `number`, `symbol`, `tuple` and -`undefined` predicates, `ArrayPlus.IsReadonly`, `IsNumeric`, the numeric literal predicates, and the -string literal and template literal predicates, each with its `IsNot*` counterpart. - -Instantiations per use without options (TypeScript 6.0, 300 distinct inputs; TypeScript 7 is within 2): - -| Type | Before | After | -| --- | ---: | ---: | -| `IsReadonly` | 40 | 26 | -| `IsArray` | 63 | 23 | -| `IsNotArray` | 63 | 23 | -| `IsBigint` | 60 | 23 | -| `IsNotBigint` | 59 | 23 | -| `IsBigintLiteral` | 58 | 36 | -| `IsNotBigintLiteral` | 60 | 38 | -| `IsBoolean` | 60 | 23 | -| `IsNotBoolean` | 59 | 23 | -| `IsTrue` | 51 | 23 | -| `IsNotTrue` | 53 | 23 | -| `IsFalse` | 51 | 23 | -| `IsNotFalse` | 53 | 23 | -| `IsFunction` | 58 | 24 | -| `IsNotFunction` | 59 | 24 | -| `IsNull` | 51 | 23 | -| `IsNotNull` | 53 | 23 | -| `IsNumber` | 58 | 23 | -| `IsNotNumber` | 58 | 23 | -| `IsNumberLiteral` | 60 | 38 | -| `IsNotNumberLiteral` | 60 | 38 | -| `IsNumeric` | 52 | 23 | -| `IsNotNumeric` | 52 | 23 | -| `IsIntegerLiteral` | 84 | 62 | -| `IsNotIntegerLiteral` | 85 | 63 | -| `IsPositiveLiteral` | 84 | 62 | -| `IsNotPositiveLiteral` | 85 | 63 | -| `IsNegativeLiteral` | 84 | 62 | -| `IsNotNegativeLiteral` | 85 | 63 | -| `IsStringLiteral` | 67 | 39 | -| `IsNotStringLiteral` | 68 | 40 | -| `IsTemplateLiteral` | 74 | 45 | -| `IsNotTemplateLiteral` | 76 | 45 | -| `IsSymbol` | 51 | 23 | -| `IsNotSymbol` | 53 | 23 | -| `IsTuple` | 51 | 24 | -| `IsNotTuple` | 52 | 24 | -| `IsUndefined` | 51 | 23 | -| `IsNotUndefined` | 53 | 23 | diff --git a/.changeset/pre/predicate-fn-701.md b/.changeset/pre/predicate-fn-701.md deleted file mode 100644 index fa500c101b..0000000000 --- a/.changeset/pre/predicate-fn-701.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'type-plus': minor ---- - -Add `.$Fn` to every one-input predicate, so each of them can be passed to `Filter`, `Find`, `Some` and `DropMatch`. - -The special-type, primitive, numeric and structure predicates (`IsAny` … `IsUnion`), `HasNull`, `HasUndefined`, `HasVoid`, and every `IsNot…` counterpart, including `IsNotObject`, now expose `.$Fn<$O>`, the same shape as `IsObject.$Fn`: - -```ts -type R = TuplePlus.Filter<[1, 'a', 2.5], IsInteger.$Fn> // [1] -type R = $Fn.Apply // true -``` - -Prefer `IsNotX.$Fn` over `$Fn.Not`: it costs about 110 fewer instantiations per `Filter` call on a 10-entry tuple (TypeScript 6.0). diff --git a/.changeset/pre/predicate-fn-742.md b/.changeset/pre/predicate-fn-742.md deleted file mode 100644 index 1e8c59209e..0000000000 --- a/.changeset/pre/predicate-fn-742.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'type-plus': minor ---- - -Add `If.$Fn` and `ArrayPlus.IsIndexOutOfBound.$Fn`, so both can be passed to `Filter`, `Find`, `Some` and `DropMatch`. - -`If.$Fn` sends an input that is not a `boolean` to `$else`. -`ArrayPlus.IsIndexOutOfBound.$Fn` fixes the array and takes the index as its input; an input that is not a `number` goes to `$else`. diff --git a/.changeset/pre/predicate-options-720.md b/.changeset/pre/predicate-options-720.md deleted file mode 100644 index 89efdf94cd..0000000000 --- a/.changeset/pre/predicate-options-720.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -'type-plus': major ---- - -Move `ArrayPlus.IsReadonly`, `IsEmptyObject`, `IsRecord` and `IsDisjoint` onto `$O` (#720). - -Each now takes `$O extends $StrictOptions<$O, X.$Options> = {}` and exports `$Options`, `$Default`, -`$Branch` and `$Fn`, so it gains `$then`/`$else`, `{ selection: 'filter' }` and the `$Then`/`$Else` -branch selectors. `IsDisjoint.$Fn` fixes `B` and takes `A` as the input, like the other two-input -predicates. The default call form of `IsEmptyObject`, `IsRecord` and `IsReadonly` gives the same result -as before. - -**Breaking: `ArrayPlus.IsReadonly` drops its legacy options.** -`IsReadonly.Options` and `IsReadonly.DefaultOptions` are replaced by `IsReadonly.$Options` and -`IsReadonly.$Default`. The `$notArray` branch is removed: a value that is not an array now resolves to -`$else`, and passing `$notArray` is a compile error. The `$never` branch stays, and `$any`, `$unknown` -and `$void` are added. - -**Behavior change: `IsDisjoint` now always returns `true` or `false`.** -It checks `keyof A & keyof B` for shared keys, which fixes two wrong results: - -| | before | after | -| --- | --- | --- | -| `IsDisjoint<{ a: 1; c: 1 }, { a: 1; b: 1 }>` (each side has a key the other lacks) | `boolean` | `false` | -| `IsDisjoint<{ a: 1 }, {}>` | `never` | `true` | - -`ANotB`, `KeysWithDiffType`, `LeftJoin` and `Merge` only test `IsDisjoint extends true`, so -their results do not change. - -Migration: - -| Before | After | -| --- | --- | -| `IsReadonly` | `IsReadonly` | -| `IsReadonly.DefaultOptions` | `IsReadonly.$Default` | -| `IsReadonly` | `IsArray; $else: X }>` | -| `IsReadonly` | `IsReadonly` | diff --git a/.changeset/pre/predicate-options-parity.md b/.changeset/pre/predicate-options-parity.md deleted file mode 100644 index da97e3083e..0000000000 --- a/.changeset/pre/predicate-options-parity.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'type-plus': major ---- - -Remove `IsStrictFunction` and `IsNotStrictFunction`. Use `IsFunction` and `IsNotFunction` instead; they give the same result for every option. - -`IsFunction` and `IsNotFunction` now honor `exact`. With `{ exact: true }` they match only the type `Function` itself, not a function signature. Before, `IsFunction` accepted `exact` and ignored it, and `IsNotFunction` rejected it. `IsFunction<() => void, { exact: true }>` is now `false`; it was `true`. - -`testType.strictFunction` still works and is now built on `IsFunction` with `exact: true`. Passing `exact: false` to it does not loosen the check. - -Every predicate with `$Options` now exports a `$Default` that spells out the value each option takes when it is left out. `IsFalse`, `IsBoolean`, `IsBigintLiteral`, `IsObject` and 46 others gained one; `IsNotFunction.$Default` now includes `$Exact.Default`. diff --git a/.changeset/pre/predicates-distribute.md b/.changeset/pre/predicates-distribute.md deleted file mode 100644 index 8b4abd7ce7..0000000000 --- a/.changeset/pre/predicates-distribute.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -'type-plus': major ---- - -The type predicates distribute over a union by default. - -A 7.x predicate checked a union as a whole, so a union with any member that failed answered `false`. -A v8 predicate checks each member and combines the answers. A union whose members pass and fail now answers `boolean`: - -```ts -type R = IsString<'a' | 1> // 7.x: false, 8.0: boolean -type R = IsTrue // 7.x: false, 8.0: boolean -type R = IsNotString<'a' | 1> // 7.x: true, 8.0: boolean -``` - -The filter form keeps the members that pass. `IsString<'a' | 1, { selection: 'filter' }>` is `'a'`, where the 7.x `StringType<'a' | 1>` was `never`. - -This applies to `IsArray`, `IsBigint`, `IsBoolean`, `IsFalse`, `IsFunction`, `IsInteger`, `IsNull`, `IsNumber`, `IsNumeric`, `IsObject`, `IsString`, `IsSymbol`, `IsTrue`, `IsTuple`, `IsUndefined`, `IsVoid`, and the `IsNot*` negation of each. -`IsPositive`, `IsNegative`, `IsNotPositive` and `IsNotNegative` already distributed in 7.x and do not change. - -A result tested with `extends true` behaves as before, because `boolean` does not extend `true`. -A result tested with `extends false`, or compared with `IsEqual`, changes. - -Migration: pass `{ distributive: false }` to keep the 7.x result. - -```ts -type R = IsString<'a' | 1, { distributive: false }> // false -type R = IsString<'a' | 1, { distributive: false; selection: 'filter' }> // never -``` - -See [Predicates distribute over unions](https://cyberuni.github.io/type-plus/guides/migrating-to-v8/#predicates-distribute-over-unions) in the migration guide. diff --git a/.changeset/pre/properties-preserve-any-unknown.md b/.changeset/pre/properties-preserve-any-unknown.md deleted file mode 100644 index f599f4f567..0000000000 --- a/.changeset/pre/properties-preserve-any-unknown.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Preserve `any` and `unknown` in `Properties` so they are returned unchanged instead of mapping to object types. Expand JSDoc with description and examples. diff --git a/.changeset/pre/purple-countries-decide.md b/.changeset/pre/purple-countries-decide.md deleted file mode 100644 index 9da0170034..0000000000 --- a/.changeset/pre/purple-countries-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Update some docs under `numerics` diff --git a/.changeset/pre/quick-grapes-travel.md b/.changeset/pre/quick-grapes-travel.md deleted file mode 100644 index afc53efefd..0000000000 --- a/.changeset/pre/quick-grapes-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Reintroduce CJS support. diff --git a/.changeset/pre/quotient-remainder-746.md b/.changeset/pre/quotient-remainder-746.md deleted file mode 100644 index 0e31361cef..0000000000 --- a/.changeset/pre/quotient-remainder-746.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'type-plus': minor ---- - -Add `Quotient` and `Remainder`, integer division on `number` and `bigint` literals, also available as `MathPlus.Quotient` and `MathPlus.Remainder`. - -`Quotient<-7, 2>` is `-3`: it truncates toward zero, as `bigint` division does at runtime. -`Remainder<-7, 2>` is `-1`: it takes the sign of the dividend, as `%` does. -A fractional input or a zero divisor resolves to `$fail` (`never` by default). diff --git a/.changeset/pre/readonly-arrays-714.md b/.changeset/pre/readonly-arrays-714.md deleted file mode 100644 index d76df15a18..0000000000 --- a/.changeset/pre/readonly-arrays-714.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'type-plus': patch ---- - -Accept readonly arrays and tuples in `Reverse`, `DropFirst`, `DropLast`, `CommonPropKeys`, `TuplePlus.CommonPropKeys` and `MapToProp`. - -These constrained their input to mutable arrays, so an `as const` tuple failed the constraint. -`Reverse`, `DropFirst` and `DropLast` keep `readonly` on the result, as `ArrayPlus.Reverse` does: -`Reverse` is `readonly [2, 1]`, and `DropFirst` is `readonly []`. -Mutable input gives the same result as before. diff --git a/.changeset/pre/red-plums-look.md b/.changeset/pre/red-plums-look.md deleted file mode 100644 index 00b51951e8..0000000000 --- a/.changeset/pre/red-plums-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Add `Merge` and `ObjectPlus.Merge` diff --git a/.changeset/pre/remove-builtin-shadows-740.md b/.changeset/pre/remove-builtin-shadows-740.md deleted file mode 100644 index f27066d852..0000000000 --- a/.changeset/pre/remove-builtin-shadows-740.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'type-plus': major ---- - -Remove the top-level `Partial`, `Required`, `Pick` and `Omit` (#740). - -**Breaking.** They shadowed the TypeScript built-ins of the same name while meaning something else, -and were deprecated aliases of the `ObjectPlus` forms. Replace each one: - -| Removed | Replacement | -| --- | --- | -| `Partial` | `ObjectPlus.Partial` | -| `Required` | `ObjectPlus.Required` | -| `Pick` | `ObjectPlus.Pick` | -| `Omit` | `ObjectPlus.Omit` | - -Import the namespace with `import type { ObjectPlus } from 'type-plus'`. - -Do not just delete the import: the file then compiles against the built-in, which differs in the -cases the `ObjectPlus` TSDoc lists. `Required` also strips `undefined` from required properties, -`Pick` and `Omit` distribute over a union `T`, and `Partial` adds `| undefined` under -`exactOptionalPropertyTypes`. diff --git a/.changeset/pre/remove-deprecated-722.md b/.changeset/pre/remove-deprecated-722.md deleted file mode 100644 index 3c6ff605d9..0000000000 --- a/.changeset/pre/remove-deprecated-722.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -'type-plus': major ---- - -Remove the deprecated exports before 8.0 GA (#722). - -**Breaking.** Each name below is gone from the package. Replace it as shown: - -| Removed | Replacement | -| --- | --- | -| `Concat`, `ArrayPlus.Concat` | the spread tuple `[...A, ...B]` | -| `KeepMatch` | `Filter` | -| `MapToProp` | `IntersectOfProps` | -| `PropUnion` | `UnionOfProps` | -| `PartialExcept` | `PartialOmit` | -| `Except` | `ObjectPlus.Omit` | -| `KeysOfOptional` | `OptionalKeys` for the optional keys, `keyof T` for the key union | -| `PromiseValue

` | the built-in `Awaited

` | -| `EitherAnd` | `EitherOrBoth` | -| `NoInfer` | TypeScript's built-in `NoInfer` (TS 5.4+) | -| `Failed`, `FailedT` | `$Error` | -| `B` namespace | `Bit` (`B.And` → `Bit.And`) | -| `NumberPlus` namespace | `NumericPlus`, plus the top-level `IsNumber` and `IsNotNumber` | -| `reduceKey()` | `reduceByKey()` | -| `unpartial()` | import `unpartial` from the `unpartial` package | -| `assertType()` and its members | `testType` for type-level checks, `x satisfies T` for compile-time assignability, `isType()` or a type guard for runtime narrowing | -| `isConstructor()` | none; it passed any function callable with `new` | -| `drop()` | none; the `DropMatch` type stays | - -`testType.Failed` is a different type and stays. - -`required()` and `requiredDeep()`, also re-exported from the `unpartial` package, stay. They now have -TSDoc and a spec: `required()` merges shallowly and lets a later `undefined` overwrite, while -`requiredDeep()` merges nested objects and keeps the earlier value over a later `undefined`. diff --git a/.changeset/pre/remove-overlapping-test-helpers.md b/.changeset/pre/remove-overlapping-test-helpers.md deleted file mode 100644 index 83043b5a49..0000000000 --- a/.changeset/pre/remove-overlapping-test-helpers.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'type-plus': major ---- - -Remove `canAssign()` and the one-argument `isType(v)`. Neither checked anything at runtime. -`testType` is the testing API for types; in code, use `satisfies`. - -| Removed | In a test | In code | -| --- | --- | --- | -| `canAssign()(v)` | `testType.canAssign(true)` | `v satisfies T` | -| `canAssign(false)(v)` | `testType.canAssign(false)` | | -| `isType(v)` | `testType.canAssign(true)` | `v satisfies T` | - -`isType(subject, validator)` is a runtime type guard and stays. diff --git a/.changeset/pre/required-omit-739.md b/.changeset/pre/required-omit-739.md deleted file mode 100644 index a1b9a6be05..0000000000 --- a/.changeset/pre/required-omit-739.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'type-plus': minor ---- - -Align `RequiredPick` and `RequiredExcept` with `PartialPick` and `PartialOmit` (#739). - -- Add `RequiredOmit`, and deprecate `RequiredExcept` as an alias of it. `Omit` names the - complement, as in `PartialOmit`. -- `RequiredPick` and `RequiredOmit` now constrain `U` to `UnionKeys` instead of `keyof T`, so `U` - may name a key of any member of a union `T`. -- Both now distribute over a union `T`. This changes the result for a union input: - - ```ts - type U = { k: 'x'; a?: 1 } | { k: 'y'; a?: 2; b?: 3 } - - type R = RequiredPick - // before: { a: 1 | 2 } & { k: 'x' | 'y' } - // after: ({ k: 'x' } & { a: 1 }) | ({ k: 'y'; b?: 3 } & { a: 2 }) - ``` - - For a non-union `T`, the result has the same properties as before. diff --git a/.changeset/pre/retire-select-invert-688.md b/.changeset/pre/retire-select-invert-688.md deleted file mode 100644 index e5c65ebf1f..0000000000 --- a/.changeset/pre/retire-select-invert-688.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -'type-plus': major ---- - -Remove `$SelectInvert` and `$SelectInvertStrict`, and export `$Void`. - -`$SelectInvert` and `$SelectInvertStrict` were an older generation of the options convention. -`IsNotNumeric`, `IsNotFunction` and `IsNotStrictFunction` now build on `$Special` and -`NotAssignable.$`, like the other `IsNot*` predicates. Their results do not change. -`IsNotNumeric.$` and `IsNotFunction.$` are new type utils that skip the special-type checks. -`IsNotNumeric.$Branch` and `IsNotFunction.$Branch` now take an optional `$O`, like their siblings. - -**Breaking** only for code that imports `$SelectInvert` or `$SelectInvertStrict`. -Use `NotAssignable.$` inside `$Special` instead, as `IsNotNumeric` does. - -`$Void` is now exported. It already appeared in public constraints such as `IsObject.$Options`, -but consumers could not name it. - -The docs now agree with the code on the default: `$O` defaults to `{}`, so a plain call such as -`IsObject` returns `true` or `false`. Pass `X.$Branch` when you compose predicates. The -`$Selection` docs no longer call `$Branch` the recommended default. Examples that used the removed -`$SelectionBranch` and `$SelectionPredicate` names now use `X.$Branch` and `$Selection.Branch`. -`IsAnyOrNever` now defaults `$O` to `{}` like the other predicates, instead of -`$Selection.Predicate`. Plain calls return the same `true`/`false`. The change also fixes -`{ selection: 'filter' }`: `IsAnyOrNever` now returns `never`. -Its examples now show the real `true`/`false` results. - -Closes [#688](https://github.com/cyberuni/type-plus/issues/688). diff --git a/.changeset/pre/rich-spies-sneeze.md b/.changeset/pre/rich-spies-sneeze.md deleted file mode 100644 index 84e5d78d6a..0000000000 --- a/.changeset/pre/rich-spies-sneeze.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": minor ---- - -Update `Equal` signature and implementation. -Deprecate `IsEqual` and `IsNotEqual` in favor of `Equal`. diff --git a/.changeset/pre/rotten-readmes-fixed.md b/.changeset/pre/rotten-readmes-fixed.md deleted file mode 100644 index 1bfdc6bda7..0000000000 --- a/.changeset/pre/rotten-readmes-fixed.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -'type-plus': patch ---- - -Correct the `src/**/readme.md` pages, which documented a v7 API that no longer exists. - -#667 deleted 13 of the 33 legacy readme pages because nothing linked to them. The surviving 20 are -linked from `packages/type-plus/readme.md` — the npm landing page — and nothing stopped them -drifting the same way. They had: **40 of their 117 source links pointed at files deleted in the v8 -rewrite**, and the prose around them documented types that are no longer exported. - -Replaced with the types that exist, each verified against the compiler: - -- `StringType` / `StrictStringType` / `IsStrictString` and their negations → - `IsString` and `{ exact: true }`. -- `FunctionType` / `StrictFunctionType` and their negations → `IsFunction` and `{ exact: true }`. -- `ObjectType`, `SymbolType`, `TupleType`, `ArrayType`, `AnyOrNeverType` and their negations → - `IsObject`, `IsSymbol`, `IsTuple`, `IsAnyOrNever`. -- `IsStrictNumber` and `IsStrictBoolean` → the `{ exact: true }` option on `IsNumber` and - `IsBoolean`. -- The retired `$SelectionBranch` → each type's own `$Branch`, or `$Selection.Branch` for the `Has*` - family, which has no namespace of its own. -- `$NeverOptions` / `$NeverBranch` / `$NeverDefault` → the `$Never.*` members that replaced them. - -Two claims were wrong on their own terms rather than merely outdated: -`IsStrictFunction` was documented as `never`; the intersection collapses to -`Function`, so it is `true`. And `src/null/readme.md` documented a type called `IsNotnull`. - -Also fixes three broken links: `./array.find_last.tsl19` (missing `#`), `./array.concat.ts` -(the file is `array_plus.concat.ts`), and `./src/mix-types/readme.md` on the npm landing page -(the directory is `mix_types`). - -`src/readme_docs.spec.ts` now pins every example on these pages with `testType.equal`, so the same -drift fails to compile next time. diff --git a/.changeset/pre/selection-predicate-namespaces-689.md b/.changeset/pre/selection-predicate-namespaces-689.md deleted file mode 100644 index b31eb09cd4..0000000000 --- a/.changeset/pre/selection-predicate-namespaces-689.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'type-plus': minor ---- - -Add `$Options`, `$Default` and `$Branch` to `HasNull`, `HasUndefined`, `HasVoid` and `IsAnyOrNever`, like the other predicates. - -Their option errors now name `$Options`, as every other predicate's do, instead of `Options`. `HasNull.$Branch`, which the `HasNull` docs already used, now exists. diff --git a/.changeset/pre/selfish-sheep-explain.md b/.changeset/pre/selfish-sheep-explain.md deleted file mode 100644 index f33dae7556..0000000000 --- a/.changeset/pre/selfish-sheep-explain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Rename `$DistributiveOptions` to `$Distributive.Options` and `$DistributiveDefault` to `$Distributive.Default`. diff --git a/.changeset/pre/separator-typo.md b/.changeset/pre/separator-typo.md deleted file mode 100644 index b4c1651580..0000000000 --- a/.changeset/pre/separator-typo.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'type-plus': patch ---- - -Rename the `Seperator` type parameter of `StringSplit` and `StringPlus.Split` to `Separator`. Type parameter names are not part of the call signature, so existing code keeps working. diff --git a/.changeset/pre/serious-deers-cheer.md b/.changeset/pre/serious-deers-cheer.md deleted file mode 100644 index 5dc66c4041..0000000000 --- a/.changeset/pre/serious-deers-cheer.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"type-plus": minor ---- - -Remove `$Exact` namespace. -`$Exact.$Options` is now `$ExactOptions`. -`$Exact.$Default` is now `$ExactDefault`. -`$Exact.$IsExact` is now `$IsExact`. - diff --git a/.changeset/pre/serious-suns-drive.md b/.changeset/pre/serious-suns-drive.md deleted file mode 100644 index c689b16407..0000000000 --- a/.changeset/pre/serious-suns-drive.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": minor ---- - -Add `Assignable`. -Deprecated `CanAssign` and `StrictCanAssign`. diff --git a/.changeset/pre/seven-peaches-explode.md b/.changeset/pre/seven-peaches-explode.md deleted file mode 100644 index 0cdc2a5e19..0000000000 --- a/.changeset/pre/seven-peaches-explode.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": minor ---- - -Rename `$Select` to `$Equal`. -Remove `$SelectStrict`, use `$Equal` instead. diff --git a/.changeset/pre/shaggy-roses-hug.md b/.changeset/pre/shaggy-roses-hug.md deleted file mode 100644 index e4ae3a66d4..0000000000 --- a/.changeset/pre/shaggy-roses-hug.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": major ---- - -Remove `NonUndefined`. Use `Exclude` instead. -Remove `NonNull`. Use `Exclude` instead. diff --git a/.changeset/pre/sharp-walls-deliver.md b/.changeset/pre/sharp-walls-deliver.md deleted file mode 100644 index 2d030256d5..0000000000 --- a/.changeset/pre/sharp-walls-deliver.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Fix `IsInteger` should returns `boolean` diff --git a/.changeset/pre/short-suns-stare.md b/.changeset/pre/short-suns-stare.md deleted file mode 100644 index b1f31fa694..0000000000 --- a/.changeset/pre/short-suns-stare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Add customize support for `ArrayPlus.Filter` diff --git a/.changeset/pre/silly-hornets-train.md b/.changeset/pre/silly-hornets-train.md deleted file mode 100644 index eb2bc7a9a9..0000000000 --- a/.changeset/pre/silly-hornets-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Updato logical types. diff --git a/.changeset/pre/silly-worms-marry.md b/.changeset/pre/silly-worms-marry.md deleted file mode 100644 index 90069d5027..0000000000 --- a/.changeset/pre/silly-worms-marry.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -'type-plus': patch ---- - -Correct the TSDoc for the numeric types. - -`IsNegative`, `IsPositive`, `IsNotNegative` and `IsNotPositive` -were documented as `boolean`. The special types are not numeric, so the sign checks -reject them: `IsNegative` and `IsPositive` are `false`, and their negations -are `true`. `IsInteger` and `IsNotInteger` were documented as `false` -and `true`; the wide `number` type contains both integers and non-integers, so both -resolve to `boolean`. - -The numeric predicates now document their `filter` selection, union distribution and -`$Branch` options in the same shape as the rest of the library, and the stale -`$SelectionBranch` name in the `number` and `bigint` examples is replaced by the -`$Branch` each type actually exports. - -The `number` examples for disabling union distribution used `number | 1`, which -TypeScript collapses to `number` — so they demonstrated nothing and stated the wrong -result. They now use `1 | string`, which is a real union. - -Every documented example across `numeric`, `number` and `bigint` is now asserted by -that type's own spec, so an example that drifts from the implementation fails to -compile. diff --git a/.changeset/pre/silver-eggs-add.md b/.changeset/pre/silver-eggs-add.md deleted file mode 100644 index d0bb8ab8b5..0000000000 --- a/.changeset/pre/silver-eggs-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Update and release as ESM package only. diff --git a/.changeset/pre/silver-maps-orient.md b/.changeset/pre/silver-maps-orient.md deleted file mode 100644 index cd2e621b55..0000000000 --- a/.changeset/pre/silver-maps-orient.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'type-plus': patch ---- - -Ship a generated `llms.txt`. - -`llms.txt` is the orientation layer for an agent meeting the library cold: what -`type-plus` is, the `Is*` / `$Options` / `$Branch` conventions that govern nearly -every type in it, and which families exist. Per-symbol detail stays in the -`.d.ts`, which the agent already has. - -The file is generated from the exported surface of `src/index.ts` by -`scripts/generate-llms-txt.mjs`, never hand-written, and `pnpm docs:llms:check` -fails the build when it drifts. It is listed in `files`, so it reaches -`node_modules/type-plus/llms.txt`, and it is also published to -`https://cyberuni.github.io/type-plus/llms.txt`. diff --git a/.changeset/pre/sixty-ears-fry.md b/.changeset/pre/sixty-ears-fry.md deleted file mode 100644 index 9b84b53ccd..0000000000 --- a/.changeset/pre/sixty-ears-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Add `$Void` support to `IsAny` and `IsNotAny`. diff --git a/.changeset/pre/slice-748.md b/.changeset/pre/slice-748.md deleted file mode 100644 index da68731469..0000000000 --- a/.changeset/pre/slice-748.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'type-plus': minor ---- - -Add `Slice`, also available as `ArrayPlus.Slice`: the type level `Array.prototype.slice()`. - -It gets the section of an array or tuple from `Start` up to, but not including, `End`, with negative indices and out-of-bound clamping as `slice()` does: `Slice<[1, 2, 3], -2>` is `[2, 3]`. diff --git a/.changeset/pre/spotty-donkeys-shave.md b/.changeset/pre/spotty-donkeys-shave.md deleted file mode 100644 index 99aa2917e5..0000000000 --- a/.changeset/pre/spotty-donkeys-shave.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -'type-plus': minor ---- - -Add `testType.has*` for asserting union membership. - -`testType.undefined()` asks whether `T` *is* `undefined`. There was no way -to ask whether `T` *contains* `undefined`, which is the common shape when a -value is optional: - -```ts -type R = number | undefined - -testType.hasUndefined(true) -``` - -Three assertions are added — `hasUndefined`, `hasNull` and `hasVoid` — backed -by the `HasUndefined`, `HasNull` and `HasVoid` predicates. `HasUndefined` -already existed; `HasNull` and `HasVoid` are new and are exported alongside it. - -All three distribute over the union explicitly, checking each branch on its -own before folding the branches back into a single answer. `HasUndefined` -keeps the results it had; the explicit distribution matters for `HasVoid`, -because `IsVoid` widens to `boolean` and would otherwise -report `number | undefined` as containing `void`. - -The `has*` methods take no options type parameter, alongside `any`, `unknown`, -`never` and `equal`: `distributive` is what the check is made of, and none of -`undefined`, `null` or `void` has a literal subtype for `exact` to narrow. Note -that `has*` is not the same as passing `{ distributive: true }` to the plain -check — distribution widens the result to `boolean`, which accepts both `true` -and `false` and so asserts nothing, while `has*` folds the branches back into a -single answer. - -Each also has a deferred form — `testType.defer.hasUndefined()` and -`testType.defer.not.hasNull()` — keeping the rule that every `testType` -check but `inspect` mirrors into `testType.defer`. - -There is no `hasAny`, `hasUnknown` or `hasNever`. A union absorbs those types, -so they can never be one branch among several, and `testType.any`, -`testType.unknown` and `testType.never` already answer the question. diff --git a/.changeset/pre/strict-option-keys-685.md b/.changeset/pre/strict-option-keys-685.md deleted file mode 100644 index 1eb51f3376..0000000000 --- a/.changeset/pre/strict-option-keys-685.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -'type-plus': major ---- - -Reject unknown option keys. - -Every type that takes an options parameter `$O` now constrains it with `$StrictOptions<$O, X.$Options>`. -A misspelled key used to compile whenever a valid key sat next to it. It is now an error that names the key and, for a prefix match, suggests the valid one: - -```ts -type R = IsObject<{}, { distributive: false; exactt: true }> -// Type 'true' is not assignable to type '"'exactt' is not a valid option. Did you mean 'exact'?"'. - -type R = IsNever<1, { distributive: false; $else: 'E' }> -// Type 'false' is not assignable to type '"'distributive' is not a valid option"'. -``` - -`IsNever`, `IsNotNever`, `IsUnknown`, `IsNotUnknown`, `IsNotFunction`, `IsNotNumeric` and `IsNotStrictFunction` already resolved a `$void` branch, -but their `$Options` did not declare it. They declare `$void` now, so `IsNever` stays accepted and answers `'V'`. - -`testType` rejects unknown keys the same way. - -New exports: - -- `$StrictOptions<$O, A>`: the constraint, for your own types. -- `$ForwardOptions<$O, A, K>`: narrows a generic wrapper's `$O` to the keys `A` accepts, so the wrapper can pass it on. -- `$ErrorMessage`: the message string type the constraint reports through. - -**Breaking** for a generic type that passes its own `$O` to a type-plus type: - -```ts -type Mine = IsObject -// error TS2344: Type '$O' does not satisfy the constraint '$StrictOptions<$O, $Options>'. -``` - -Repeat the strict constraint when your type takes the same options, -or forward through `$ForwardOptions` when it takes its own: - -```ts -type Mine = {}> = IsObject - -type Yours = {}> = - IsObject> -``` - -Do not narrow with `$O extends $StrictOptions<$O, IsObject.$Options> ? IsObject : never` in a type with options of its own: -it answers `never` whenever one of those options is passed. - -The constraint costs about 3 type instantiations per use without options and about 15 with options. -See the [migration guide](https://cyberuni.github.io/type-plus/guides/strict-options/). - -Refs [#689](https://github.com/cyberuni/type-plus/issues/689). -Closes [#685](https://github.com/cyberuni/type-plus/issues/685). diff --git a/.changeset/pre/strict-options-non-object-689.md b/.changeset/pre/strict-options-non-object-689.md deleted file mode 100644 index 8723d52d6a..0000000000 --- a/.changeset/pre/strict-options-non-object-689.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'type-plus': patch ---- - -Report a non-object options argument (`IsObject`) as `Type 'string' has no properties in common with type '$Options'`, instead of listing every member of `string` as an unknown option. diff --git a/.changeset/pre/string-plus-members-747.md b/.changeset/pre/string-plus-members-747.md deleted file mode 100644 index f111c18fad..0000000000 --- a/.changeset/pre/string-plus-members-747.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'type-plus': minor ---- - -Add the type-level forms of more `String.prototype` methods to `StringPlus`: - -- `StringPlus.StartsWith` and `StringPlus.EndsWith`: predicates shaped like `StringPlus.Includes`, with `$then`/`$else`, `selection: 'filter'` and the `$Branch` selectors. -- `StringPlus.Replace` and `StringPlus.ReplaceAll`: replace the first or every occurrence of a string pattern. - -```ts -type R = StringPlus.StartsWith<'abc', 'ab'> // true -type R = StringPlus.ReplaceAll<'a.b.c', '.', '/'> // 'a/b/c' -``` diff --git a/.changeset/pre/strong-months-behave.md b/.changeset/pre/strong-months-behave.md deleted file mode 100644 index 8f1328b82d..0000000000 --- a/.changeset/pre/strong-months-behave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Fix `IsNegative` should return `boolean` diff --git a/.changeset/pre/strong-rabbits-tie.md b/.changeset/pre/strong-rabbits-tie.md deleted file mode 100644 index f60517a552..0000000000 --- a/.changeset/pre/strong-rabbits-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Remove `Positive`, `Negative`, `NumericType`, `NotNumericType`, diff --git a/.changeset/pre/sweet-tools-clap.md b/.changeset/pre/sweet-tools-clap.md deleted file mode 100644 index 221fb48b74..0000000000 --- a/.changeset/pre/sweet-tools-clap.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"type-plus": minor ---- - -Add `$DefineInputOptions` and `$DefineBranchOptions`. -Add support of handing `$any`, `$unknown`, `$never` for `IsAny`. - -Add `$ResolveBranch` that fixes the `unknown` pass-through issue. - -The types will be able to use in the form of `IsAny`, hopefully. - -It's still not recommended doing so, but at least it will not produce weird results. - -Will need to convert other types to support that and add tests for them. diff --git a/.changeset/pre/tasty-numbers-chew.md b/.changeset/pre/tasty-numbers-chew.md deleted file mode 100644 index a06438cf12..0000000000 --- a/.changeset/pre/tasty-numbers-chew.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Add `SubUnion` type. diff --git a/.changeset/pre/test-type-callable-with-784.md b/.changeset/pre/test-type-callable-with-784.md deleted file mode 100644 index 6d2e326104..0000000000 --- a/.changeset/pre/test-type-callable-with-784.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'type-plus': minor ---- - -Add `testType.callableWith` and `testType.constructibleWith`, which check that a function can be called, or a class constructed with `new`, with a list of argument types. -The check passes when one of the overloads accepts the arguments, which `equal` on `Parameters` cannot express because it keeps only the last overload. - -```ts -function f(value: string): string -function f(value: number, radix: number): string - -testType.callableWith(true) -testType.callableWith(false) -testType.constructibleWith(true) -``` - -Both are also available on `testType.of(value)` and `testType.defer`. diff --git a/.changeset/pre/test-type-failed-names-781.md b/.changeset/pre/test-type-failed-names-781.md deleted file mode 100644 index 1861184212..0000000000 --- a/.changeset/pre/test-type-failed-names-781.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'type-plus': minor ---- - -Name the check, the actual type and the expected type when an immediate `testType` check fails. - -`testType.equal(true)` failed with `Argument of type 'true' is not assignable to parameter of type 'false'`. -It now fails with `... parameter of type 'false | Failed<"equal", string, number>'`, the same `testType.Failed` a `testType.defer` check reports. -Checks are called the same way. The new `testType.Expectation` type is the `expected` parameter of every immediate check, including `testType.property`, `testType.callableWith`, `testType.constructibleWith` and the checks `testType.of(value)` returns. diff --git a/.changeset/pre/test-type-fn-subjects-783.md b/.changeset/pre/test-type-fn-subjects-783.md deleted file mode 100644 index a9219d5e12..0000000000 --- a/.changeset/pre/test-type-fn-subjects-783.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'type-plus': minor ---- - -Add `parameters`, `returns` and `resolves` to `testType.Subject`, so `testType.of(value)` can check a function's parameters, its return type, or what a promise resolves to with any `testType` check. - -```ts -testType.of((a: number) => String(a)).parameters.equal<[a: number]>(true) -testType.of((a: number) => String(a)).returns.equal(true) -testType.of(async () => 1).returns.resolves.equal(true) -``` - -On a subject that is not a function (or not a `PromiseLike` for `resolves`), the member is a `testType.Failed` with no checks, so a check on it fails to compile. diff --git a/.changeset/pre/test-type-of-782.md b/.changeset/pre/test-type-of-782.md deleted file mode 100644 index 80af7bff3a..0000000000 --- a/.changeset/pre/test-type-of-782.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'type-plus': minor ---- - -Add `testType.of(value)`, which checks the type of a value without naming a variable and writing `typeof`. -It returns a `testType.Subject` holding every `testType` check with the subject bound to the type of `value`. - -```ts -testType.of([1, 2].map(String)).equal(true) -testType.of('a' as const).string<{ exact: true }>(false) -``` - -An inline literal widens as in any generic call, so write `as const` to keep it narrow. diff --git a/.changeset/pre/test-type-property-785.md b/.changeset/pre/test-type-property-785.md deleted file mode 100644 index 54d594565b..0000000000 --- a/.changeset/pre/test-type-property-785.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'type-plus': minor ---- - -Add `testType.property(expected)`, which checks that type `T` has the key `K`. -It is `testType.true>(expected)` under a name, with a deferred form and a `testType.of(value).property()` counterpart. - -```ts -testType.property<{ a?: 1 }, 'a'>(true) -testType.property<{ a: 1 }, 'a' | 'b'>(false) // every key in `K` must be present -testType.of({ a: 1 }).property<'a'>(true) -``` diff --git a/.changeset/pre/then-else-to-options-665.md b/.changeset/pre/then-else-to-options-665.md deleted file mode 100644 index 73d6814037..0000000000 --- a/.changeset/pre/then-else-to-options-665.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -'type-plus': major ---- - -Move the last positional `Then`/`Else` types onto the `$Options` object (#665). - -`If`, `HasKey`, `IsOptionalKey`, `IsUnion`, `ArrayPlus.IsIndexOutOfBound` and -`StringPlus.Includes` were the remaining branching types taking their two -outcomes as positional type parameters. They now take `$O`, like the rest of -the `IsXXX` family, which also gives them `{ selection: 'filter' }` and the -`$Branch` selectors for free. - -Following #661, the positional parameters are replaced in place rather than -kept alongside the options object: position 3 cannot hold either a `Then` or an -options object without guessing which one a caller meant. - -Migration: - -| Before | After | -| --- | --- | -| `If` | `If` | -| `HasKey` | `HasKey` | -| `IsOptionalKey` | `IsOptionalKey` | -| `IsUnion` | `IsUnion` | -| `UnionType` | `IsUnion` | -| `UnionType` | `IsUnion` | -| `IsIndexOutOfBound` | `IsIndexOutOfBound` | -| `StringPlus.Includes` | `StringPlus.Includes` | - -`UnionType` is removed rather than migrated. It defaulted to -`Then = T, Else = never`, which is filter semantics, and `$O` spells that -natively — so the `UnionType` / `IsUnion` pair collapses into one type plus an -option, the same collapse #659 applied to the `LooseArrayType` family. - -The default call form of every type is unchanged: `IsUnion` is still -`true`/`false`, `IsIndexOutOfBound` is still `true`/`false`. - -The filter subject is the value being checked, which for the two key -predicates is the key rather than the record. That makes the filter form a key -selection: `IsOptionalKey` is -`OptionalKeys`. - -`StringIncludes` keeps its positional `Then`/`Else`. It is the low-level string -check that `StringPlus.Includes` is built on, and was not part of #665's sweep. diff --git a/.changeset/pre/three-pants-hunt.md b/.changeset/pre/three-pants-hunt.md deleted file mode 100644 index 23aef9a925..0000000000 --- a/.changeset/pre/three-pants-hunt.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": minor ---- - -Change `$ResolveBranch` type parameters order. -`T` is moved to the last position as `D` (as it is the default value), and made optional. diff --git a/.changeset/pre/tidy-coins-relate.md b/.changeset/pre/tidy-coins-relate.md deleted file mode 100644 index 58da3f0066..0000000000 --- a/.changeset/pre/tidy-coins-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Update `IsTuple` and variances. diff --git a/.changeset/pre/tidy-mirrors-shave.md b/.changeset/pre/tidy-mirrors-shave.md deleted file mode 100644 index b6bbe06b72..0000000000 --- a/.changeset/pre/tidy-mirrors-shave.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'type-plus': patch ---- - -Update runtime dependencies: `tersify` `^3.11.1` -> `^4.0.6` and `unpartial` `^1.0.4` -> `^1.0.7`. - -Both are runtime `dependencies` of `type-plus`, so this changes what consumers resolve. - -Two consumer-visible consequences, neither of which touches the type-level API: - -- `assertType`'s failure message renders the validator through `tersify`, and `tersify@4` - preserves the quote style of the original source instead of normalising it to double - quotes. A validator written as `subject => typeof subject === 'boolean'` now reports - `subject fails to satisfy subject => typeof subject === 'boolean'` rather than - `... === "boolean"`. Only the diagnostic string changed; the assertion behaviour did not. -- `unpartial@1.0.7` declares `engines: { node: '>= 20' }`, so the effective Node floor for - `type-plus`'s runtime dependencies is Node 20. `tersify@4` also targets ES2020 (it was - ES5 in v3). diff --git a/.changeset/pre/tidy-moons-shave.md b/.changeset/pre/tidy-moons-shave.md deleted file mode 100644 index 25c6ec1eea..0000000000 --- a/.changeset/pre/tidy-moons-shave.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -'type-plus': minor ---- - -`testType` type checks accept options. - -Each check now takes an optional second type parameter carrying the behavioral -options of the `IsXXX` type behind it — `testType.$Options` is -`{ distributive?: boolean; exact?: boolean }`. `canAssign` and -`strictCanAssign` take it as their third and accept `distributive` only. - -```ts -testType.string<'a', { exact: true }>(false) -testType.array<[string], { exact: false }>(true) -testType.string<'a' | 1, { distributive: true }>(true) -testType.strictCanAssign(true) -``` - -Options are merged over each method's own defaults, so the existing -no-options call form keeps the behavior it has always had. The parameter -defaults to `{}` and sits after the type under test, so inference at existing -call sites is unchanged — the whole suite type-checks unmodified on TypeScript -5.4 through 7. - -`any`, `unknown`, `never` and `equal` take no options — none of the types -behind them has a distributive or exact dimension. A new guide, -[Migrating from Then/Else to $Options][migration], documents how that older -signature maps onto `$O` and which types have yet to move. - -[migration]: https://cyberuni.github.io/type-plus/guides/migrating-then-else-to-options/ diff --git a/.changeset/pre/tidy-pans-search.md b/.changeset/pre/tidy-pans-search.md deleted file mode 100644 index b35108fa76..0000000000 --- a/.changeset/pre/tidy-pans-search.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -'type-plus': patch ---- - -Retire the `*_docs.spec.ts` convention. A test belongs with the behavior it -checks, not with where the claim came from. - -#662 introduced one file per family collecting every assertion that mirrored a -TSDoc `@example`, and #672 through #674 extended it. That split the suite along -the wrong axis: `IsNumber`'s union behavior was asserted in both -`is_number.spec.ts` and `number_docs.spec.ts`, and a reader of either had no -way to know the other existed. - -All twenty of those files are dissolved into the per-symbol specs, each -assertion placed under the test that already covers that behavior. Most were -duplicates and are simply gone — 214 of them in `number` and `numeric` alone, -where the per-symbol specs already made every claim. What survived is what the -per-symbol specs were missing, and pulling it across exposed real gaps: -`Increment` and `Decrement` had no test anywhere, `Widen` had no spec file at -all, and `Abs` covered neither zero nor a floating-point input. - -`llms.txt` no longer claims that a named list of families has every example -pinned. That sentence was derived from a glob for `*_docs.spec.ts`, which only -ever proved the file existed, and there is no honest way to compute it now. -`AGENTS.md` states the rule that replaces it. diff --git a/.changeset/pre/tidy-pugs-repeat.md b/.changeset/pre/tidy-pugs-repeat.md deleted file mode 100644 index ffab085428..0000000000 --- a/.changeset/pre/tidy-pugs-repeat.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -'type-plus': patch ---- - -Stop emitting `esm/package.json`, and declare `"sideEffects": false` in the -`cjs/package.json` marker. - -The package root already declares `"type": "module"`, so `esm/*.js` are ESM by -inheritance — the marker restated what was already true. It was also costing -consumers tree-shaking. Bundlers read `sideEffects` from the nearest -`package.json` describing a module, and `esm/package.json` became that file for -every ESM entry point while carrying no `sideEffects` field, so webpack fell -back to assuming side effects. Bundling an ESM consumer that imports a single -symbol produced 32,779 bytes across 21 modules with the file present and 707 -bytes across 2 modules without it. Rollup with `@rollup/plugin-node-resolve` -produced byte-identical output either way — its own static analysis drops the -unused modules regardless — so this was a webpack-class regression, not a -universal one. - -The `cjs/package.json` marker stays: without it Node reads `cjs/index.js` as -ESM and its `require()` calls resolve against the caller. It now also carries -`"sideEffects": false`, since it shadows the root field for CJS consumers the -same way. diff --git a/.changeset/pre/tough-files-sort.md b/.changeset/pre/tough-files-sort.md deleted file mode 100644 index ad11027b0b..0000000000 --- a/.changeset/pre/tough-files-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": major ---- - -Update `IsString` and its variances. diff --git a/.changeset/pre/tough-forks-dance.md b/.changeset/pre/tough-forks-dance.md deleted file mode 100644 index 16dd4480a9..0000000000 --- a/.changeset/pre/tough-forks-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Add `AdjustExactOptionalProps`. diff --git a/.changeset/pre/transform-options-721.md b/.changeset/pre/transform-options-721.md deleted file mode 100644 index c6bbe960cb..0000000000 --- a/.changeset/pre/transform-options-721.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -'type-plus': major ---- - -Give the transforms the options convention the predicates use. - -Every transform that took options or a positional fallback now takes one last type parameter -`$O extends $StrictOptions<$O, X.$Options> = {}`. -Its namespace declares the keys in `X.$Options` and their defaults in `X.$Default`, -and an unknown key is an error that names the key. -A `$`-prefixed key names what the type returns in one case. -The convention is written up in the [Options reference](https://cyberuni.github.io/type-plus/reference/options/). - -Migration: - -| Before | After | -| --- | --- | -| `Head`, same for `Last`, `DropFirst`, `DropLast` | `Head` | -| `IndexAt` | `IndexAt` | -| `Add`, and the same third parameter on `Subtract`, `Multiply`, `GreaterThan`, `Max` | `Add` | -| `Abs`, `StringToNumber`, `StringToBigint`, `StringToNumeric` | `Abs` | -| `At`, `ArrayPlus.At` | `At` | -| `CreateTuple` | `CreateTuple` | -| `Exclude` | `Exclude` | -| `NotUnknownOr` | `NotUnknownOr` | -| `ObjectPlus.Merge` | `ObjectPlus.Merge`; the parameter was never honored | -| `TypePlusOptions.NotArray` | removed; declare `$notArray?: unknown` on your own `$Options` | -| `X.Options`, `X.DefaultOptions` on `Head`, `Last`, `FindFirst`, `DropFirst`, `DropLast`, `CommonPropKeys` (top-level, `ArrayPlus`, `TuplePlus`), `Box`, `ArrayPlus.Find`, `ArrayPlus.Filter`, `ArrayPlus.IndexAt`, `ArrayPlus.ElementMatch`, `TuplePlus.Find` | `X.$Options`, `X.$Default` | - -The `$never`, `$array`, `$tuple`, `$notArray`, `$notBoxable`, `$notMatch`, `$widen`, `$unionNotMatch` and `widen` keys keep their names. - -`StringIncludes` keeps its positional `Then` and `Else`; `StringPlus.Includes` is the one with options. - -Two fixes ride along: -the top-level `CommonPropKeys` now honors `$never` for an array input, and -`StringToNumber` passes `$fail` through when it strips trailing zeros. - -A new `$Fail` namespace (`$Fail.$Options`, `$Fail.$Default`) gives a custom transform the `$fail` key. diff --git a/.changeset/pre/tricky-wasps-glow.md b/.changeset/pre/tricky-wasps-glow.md deleted file mode 100644 index 7acaaa5cec..0000000000 --- a/.changeset/pre/tricky-wasps-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Add `Box` to box primitive types to their boxed types. diff --git a/.changeset/pre/tsdoc-fixes-736.md b/.changeset/pre/tsdoc-fixes-736.md deleted file mode 100644 index 06eadb900e..0000000000 --- a/.changeset/pre/tsdoc-fixes-736.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'type-plus': patch ---- - -Fix TSDoc copy-paste bugs and tag drift. - -The `$` type util docs of `IsNotBigintLiteral`, `IsNotSymbol`, `NotAssignable`, `IsNotTuple`, `IsNotObject`, `IsVoid`, `IsNotVoid`, `IsNotUndefined`, `IsNotStringLiteral` and `IsNumeric` described the wrong type or dropped the negation; they now describe what each checks. -`IsTemplateLiteral`, `IsNotTemplateLiteral`, `IsAny` and the `logical` predicates now use the same `🎭 *predicate*` and `🔢 *customize*` tags as every other predicate. - -`EitherOrBoth`, `PrimitiveTypes`, `ComposableTypes` and `NonComposableTypes` now carry a tag and a pinned `@example`. diff --git a/.changeset/pre/tsdoc-gaps-718.md b/.changeset/pre/tsdoc-gaps-718.md deleted file mode 100644 index ced9c1cfa2..0000000000 --- a/.changeset/pre/tsdoc-gaps-718.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'type-plus': patch ---- - -Fill TSDoc and `@example` gaps on public exports. - -Newly documented: `ArrayPlus.PadStart`, `stub.build` and `stub.builder`. -The `ArrayPlus.Reverse` doc comment was a plain `/*` comment and did not show up in the `.d.ts`; it now does. -`stub.build` and `stub.builder` carry their TSDoc on a merged `stub` namespace, because the `.d.ts` emit dropped it from the expando assignments. - -Added `@example`s to `At`, `Tail`, `UnionKeys`, `UnionOfValues`, `SubUnion`, `Widen`, `Partial`, `PartialPick`, `PartialOmit`, `Pick`, `Omit`, `OptionalProps`, `ExtractFunction`, `ExcludePropType`, `KeyTypes`, `AdjustExactOptionalProps`, `RecursiveIntersect`, `And`, `Or`, `Not`, `Xor`, `Brand`, `Flavor`, `StringToNumber`, `StringToBigint`, `StringToNumeric`, `NumericToString`, and the `$Any`, `$Never`, `$Unknown` and `$Void` markers, whose examples were pseudo-code before. -A `testType.equal` in each symbol's spec pins every example. - -Fixed two wrong examples: the `selection: 'filter'` example of `HasUndefined` left out the option, and one `PadStart` line said it showed an ignored `MaxLength` but showed padding. diff --git a/.changeset/pre/twelve-shirts-lay.md b/.changeset/pre/twelve-shirts-lay.md deleted file mode 100644 index 617ab8acf5..0000000000 --- a/.changeset/pre/twelve-shirts-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": patch ---- - -Remove `$Exact` branch type. It is not needed anymore. diff --git a/.changeset/pre/two-input-predicate-fn-701.md b/.changeset/pre/two-input-predicate-fn-701.md deleted file mode 100644 index 767b6bc9ef..0000000000 --- a/.changeset/pre/two-input-predicate-fn-701.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'type-plus': minor ---- - -Add `.$Fn` to the two-input predicates `Assignable`, `NotAssignable`, `Equal`, `HasKey` and `IsOptionalKey`. - -A type function has one input, so the entry being checked is the function's input and the other input is fixed as `.$Fn`'s first type parameter, ahead of the options: - -```ts -type R = TuplePlus.Filter<[1, number, 1], Equal.$Fn<1>> // [1, 1] -type R = TuplePlus.Find<[string, 1], Assignable.$Fn> // 1 -type R = TuplePlus.Filter<[{ a: 1 }, {}], HasKey.$Fn<'a'>> // [{ a: 1 }] -``` - -`Equal.$Fn` gives `Filter`, `Find` and `DropMatch` an exact match, which is what `Some`'s `'strict'` mode does. diff --git a/.changeset/pre/two-shrimps-wonder.md b/.changeset/pre/two-shrimps-wonder.md deleted file mode 100644 index 9eb66d606f..0000000000 --- a/.changeset/pre/two-shrimps-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Update `IsObject` and variants diff --git a/.changeset/pre/type-function-fn-687.md b/.changeset/pre/type-function-fn-687.md deleted file mode 100644 index 1e8cdfb6bf..0000000000 --- a/.changeset/pre/type-function-fn-687.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'type-plus': minor ---- - -Let the collection types take type-plus predicates as type functions. - -`$Fn` is a type function and `$Fn.Apply` calls it. -`TuplePlus.Filter`, `TuplePlus.Find`, `ArrayPlus.Find`, `Some`, `DropMatch` (and `TuplePlus.DropMatch`, `ArrayPlus.DropMatch`) accept either a plain type, matched with `extends` as before, or a `$Fn`, which matches when it returns `true`. -`IsObject.$Fn<$O>` is `IsObject` as a type function with its options applied, and `$Fn.Not` negates one. - -```ts -type R = TuplePlus.Filter<[1, { a: 1 }, 'x', object], IsObject.$Fn<{ exact: true }>> // [object] -type R = TuplePlus.Filter<[1, { a: 1 }, 'x', object], $Fn.Not> // [1, 'x'] -``` - -A plain type costs about 10 more instantiations per `Filter` call on a 10-entry tuple (TypeScript 6.0, 100 distinct inputs). -Calling a `$Fn` costs about 6 per entry on top of the predicate itself. diff --git a/.changeset/pre/v7-to-v8-migration-guide.md b/.changeset/pre/v7-to-v8-migration-guide.md deleted file mode 100644 index f2e0219419..0000000000 --- a/.changeset/pre/v7-to-v8-migration-guide.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'type-plus': patch ---- - -Link the readme to the new [v7 to v8 migration guide](https://cyberuni.github.io/type-plus/guides/migrating-to-v8/), which lists every breaking change with a before and after example. - -Correct the `ObjectPlus.Merge` signature in `src/object/readme.md`: it takes `A` and `B` only, since its unused options parameter was removed in 8.0. diff --git a/.changeset/pre/v8-beta-notice.md b/.changeset/pre/v8-beta-notice.md deleted file mode 100644 index ce6bf0a524..0000000000 --- a/.changeset/pre/v8-beta-notice.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'type-plus': patch ---- - -Document the v8 beta status in the readme. - -v8 is in active development again on the `beta` dist-tag, and breaking changes are -expected between beta releases. The readme now says so, and tells consumers on the beta -to pin an exact version and upgrade deliberately, or to stay on the latest official -release until v8 is stable. diff --git a/.changeset/pre/whole-number-results-766.md b/.changeset/pre/whole-number-results-766.md deleted file mode 100644 index da669d96c0..0000000000 --- a/.changeset/pre/whole-number-results-766.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'type-plus': patch ---- - -A whole-number result from fractional inputs is now a numeric literal. - -`Add<1.5, 2.5>` is `4`, `Subtract<1.5, 0.5>` is `1` and `Multiply<0.5, 4>` is `2`. -Each used to resolve to an error string such as `"The value '4.0' cannot be represented as bigint or number"`. -`GreaterThan`, `GreaterThanOrEqual`, `LessThan`, `LessThanOrEqual`, `Max` and `Min` now compare a fractional pair whose difference is a whole number: `GreaterThan<1.5, 2.5>` is `false` instead of `never`. diff --git a/.changeset/pre/wild-apes-shave.md b/.changeset/pre/wild-apes-shave.md deleted file mode 100644 index cc5b46d142..0000000000 --- a/.changeset/pre/wild-apes-shave.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'type-plus': patch ---- - -Document the 36 undocumented exports in `src/object/`. - -Every `@example` is pinned by a compiled assertion in -`src/object/object_docs.spec.ts`, so an example that drifts from the -implementation fails to build. - -Four of the newly documented types do not do what their names say. The -documentation records the behavior rather than changing it: - -- `KnownKeys` resolves to `never` for every `T` on every supported - TypeScript version. -- `KeysOfOptional` returns `keyof T` when every property is required, and - `never` as soon as one is optional. -- `RecursiveRequired` does not descend into optional properties. -- `hasKey()` and `hasProperty()` test truthiness rather than key presence, and - `getField()` replaces any falsy value with the default. diff --git a/.changeset/pre/wise-zoos-jog.md b/.changeset/pre/wise-zoos-jog.md deleted file mode 100644 index 4fed6d8444..0000000000 --- a/.changeset/pre/wise-zoos-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"type-plus": minor ---- - -Export `$Exact`. diff --git a/.changeset/stable-8-docs.md b/.changeset/stable-8-docs.md deleted file mode 100644 index d3bbf2d4ff..0000000000 --- a/.changeset/stable-8-docs.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"type-plus": patch ---- - -Drop the beta wording from the readme and summarize what changes from 7.x. -Correct the TSDoc examples of `IsBoolean`, `IsEqual`, `IsNull`, `IsSymbol`, `IsUndefined`, `IsVoid`, `Assignable` and `NotAssignable` that did not match what the types return. diff --git a/.changeset/stub-later-with-wins.md b/.changeset/stub-later-with-wins.md deleted file mode 100644 index ba905c4e64..0000000000 --- a/.changeset/stub-later-with-wins.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"type-plus": major ---- - -Let a later `stub.builder().with()` object win over an earlier initializer on the same key. - -Before, a later `.with()` object only filled in keys the earlier initializers left unset, so `stub.builder({ a: 1 }).with({ a: 5 }).create()()` returned `{ a: 1 }`. It now returns `{ a: 5 }`. Objects are deep-merged, so nested keys a later object does not set are kept. - -The partial stub passed to the created function still wins over every object initializer, and a function initializer still receives the stub so far and returns the next one. - -Migration: -- If you relied on an earlier `.with()` value surviving a later one, reorder the `.with()` calls or drop the key from the later object. diff --git a/.changeset/testing-entry.md b/.changeset/testing-entry.md deleted file mode 100644 index 6c8a7846ee..0000000000 --- a/.changeset/testing-entry.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"type-plus": major ---- - -Move `testType` and `stub` to the `type-plus/testing` entry. `type-plus` no longer exports them, so production code does not pay to parse them or see them in auto-import. - -Migration: -- Replace `import { stub, testType } from 'type-plus'` with `import { stub, testType } from 'type-plus/testing'`. -- Under `moduleResolution: "node"`, the `typesVersions` field resolves the types of `type-plus/testing`. diff --git a/packages/type-plus/CHANGELOG.md b/packages/type-plus/CHANGELOG.md index 554ab2b43a..ce5a52aa20 100644 --- a/packages/type-plus/CHANGELOG.md +++ b/packages/type-plus/CHANGELOG.md @@ -1,5 +1,1550 @@ # type-plus +## 8.0.0 + +### Major Changes + +- 9a2ff05: Ship ESM only. The CommonJS build in `cjs/` is gone, and so is the `require` export condition. + + Both builds were about 4.5 MB each, so every install, including transitive ones, carried a second copy of the runtime. + `require('type-plus')` still works on Node.js 20.19 and later, and 22.12 and later, which load ES modules through `require()` without a flag. + `engines.node` now declares that floor: `^20.19.0 || >=22.12.0`. +- 8c1d53f: Fix the JSON types and add `JsonCompatible`, `IsJson` and `IsNotJson`. + + - `JsonArray` is readonly, so readonly arrays, tuples and `as const` data are accepted as `JsonTypes`. + - `JsonObject` rejects a property holding `undefined` (`{ a: undefined }`). An optional property (`{ a?: number }`) is still accepted. A read from it is `JsonTypes | undefined` under `noUncheckedIndexedAccess`, and `JsonTypes` without it. + - Add `JsonCompatible`: constrain with `T extends JsonCompatible` to accept any JSON-safe value, including an `interface`, which TypeScript never lets satisfy `JsonTypes`. The compiler error names the path of the part that is not JSON, such as `"not JSON at $.profile.createdAt"`. + - Add the `IsJson` and `IsNotJson` predicates, with the usual `selection`, `distributive` and branch options. + - Remove `JsonTypes.get` and the `JsonTypes` and `JSONTypes` values. `type-plus` only ever exported them as types, so they were unreachable. The deprecated `JSONTypes` type alias stays until 9.0. + + Migration: + - Replace `JsonTypes.get(obj, 'a', 'b')` with a plain property read and a cast. + - Replace a `T extends JsonTypes` constraint that has to accept interfaces with `T extends JsonCompatible`. +- ae0797e: Let a later `stub.builder().with()` object win over an earlier initializer on the same key. + + Before, a later `.with()` object only filled in keys the earlier initializers left unset, so `stub.builder({ a: 1 }).with({ a: 5 }).create()()` returned `{ a: 1 }`. It now returns `{ a: 5 }`. Objects are deep-merged, so nested keys a later object does not set are kept. + + The partial stub passed to the created function still wins over every object initializer, and a function initializer still receives the stub so far and returns the next one. + + Migration: + - If you relied on an earlier `.with()` value surviving a later one, reorder the `.with()` calls or drop the key from the later object. +- 6b19c44: Move `testType` and `stub` to the `type-plus/testing` entry. `type-plus` no longer exports them, so production code does not pay to parse them or see them in auto-import. + + Migration: + - Replace `import { stub, testType } from 'type-plus'` with `import { stub, testType } from 'type-plus/testing'`. + - Under `moduleResolution: "node"`, the `typesVersions` field resolves the types of `type-plus/testing`. +- 78c4ac8: Answer `any`, `unknown`, `never` and `void` with TypeScript's own assignability relation in + `CanAssign`, `IsAssign`, `StrictCanAssign`, `Assignable` and `NotAssignable`. + + `CanAssign` was a bare `A extends B` behind a `boolean` guard. `extends` is not + assignability: it gets all three special types wrong, and `boolean extends A` swallowed `any` + and `unknown` on the way past. The rule now applied everywhere is the compiler's: + + - `any` is assignable to every type except `never`, and every type is assignable to `any`. + The relation is **symmetric** for `any`, exactly as it is in TypeScript — both + `const b: number = a` and `const c: any = n` compile. + - `unknown` is the top type: everything is assignable to it, and it is assignable only to + `any` and `unknown`. + - `never` is the bottom type: it is assignable to everything, and nothing but `never` is + assignable to it. + - `void` is not special to the relation and is answered structurally. + - Unknown-like unions such as `{} | null | undefined`, which TypeScript relates to `unknown` in + both directions, are compared structurally rather than answered as `unknown`. + + Before / after: + + ```ts + type R = CanAssign // was false, now true <- #349 + type R = CanAssign // was never, now true + type R = CanAssign // was never, now true + type R = CanAssign // was never, now true + type R = CanAssign // was never, now true + + type R = StrictCanAssign // was true, now false + type R = Assignable // was true, now false + type R = NotAssignable // was false, now true + + type R = Assignable<1, void> // was true, now false + type R = Assignable // was true, now false + type R = NotAssignable // was true, now false + ``` + + `Assignable` and `NotAssignable` had no `$void` branch at all, so a `void` on either side fell + through `$Special` to an unconditional `$then`. That is why the two of them, which are supposed to + be complements, both answered `true` for `<1, void>` and both answered `true` for ``. + + Unchanged: distribution over unions (`CanAssign` is still `boolean`), + the `boolean` special case (`CanAssign` is still `false`), the `$any` / `$unknown` + / `$never` branch overrides, and `Assignable.$` / `IsExtend`, which remain the raw + `A extends B` forms for anyone who wants the unadorned behavior. + + Closes [#349](https://github.com/unional/type-plus/issues/349). +- a27e32e: Update `IsSymbol` and variances +- bee62fe: Make the branch markers named interfaces, and declare every predicate's `$Options` as an interface. + + `$Then`, `$Else`, `$Any`, `$Unknown`, `$Never`, `$NotNever` and `$Void` were string subtypes built + on `$Type`. They are now interfaces extending `$Branch`, which carries the branch name in a + namespaced string key: + + ```ts + export interface $Branch

{ + readonly '~type-plus/branch': P + } + export interface $Then extends $Branch<'$then'> {} + ``` + + `$Branch.$Key` names that key (`'~type-plus/branch'`), so type-level code can read a marker's + name with `$B[$Branch.$Key]` without spelling the key out. + + Markers print by name in hovers and errors, are no longer strings, and stay distinct from each other: + + ```ts + const a: $Then = 1 + // was: Type '1' is not assignable to type '$<"branch", "$then"> & "$then"'. + // now: Type 'number' is not assignable to type '$Then'. + + type R = IsString<$Then> // was true, now false + ``` + + The `$Options` of each predicate (`IsObject.$Options`, `IsNever.$Options`, `Assignable.$Options`, + and 60 more) is now an `interface … extends …` instead of an intersection alias. Expanding one + now shows `$Options` instead of `$Selection.Options & … & $InputOptions<($<"branch", "$any"> & "$any") | …>`, + and each use costs 2 to 4 fewer type instantiations. + + **Breaking** only for code that: + + - reads `_$value` (or `$Type.$ValueKey`) on a marker — read `$Branch.$Key` instead, or match + the marker with `extends`; + - relies on a marker being a string, such as `$Then extends string` or `$Then extends '$then'`. + + Refs [#689](https://github.com/cyberuni/type-plus/issues/689). + Closes [#684](https://github.com/cyberuni/type-plus/issues/684). +- 3957e31: Update `IsUnknown` and `IsNotUnknown`. Remove `UnknownType` and `NotUnknownType`. +- 20f127c: Rename `$ExtractManipulatedString` to `ExtractManipulatedString`. + + A leading `$` marks the type-branching machinery (`$Then`, `$Else`, `$Selection`, `X.$Options`). + `ExtractManipulatedString` is a string util that takes no part in it, so the prefix misled. + The type is new in 8.0, so no stable release is affected. + + Migration: replace `$ExtractManipulatedString` with `ExtractManipulatedString`. +- 1069119: Remove the types and functions deprecated in v7. + + | Removed | Replacement | + | --- | --- | + | `First` | `FindFirst`, `ArrayPlus.Find` | + | `isType.t()` | `isType()`, `testType.true()` | + | `isType.f()` | `isType()`, `testType.false()` | + | `isType.never()` | `isType()`, `testType.never()` | + | `isType.equal()` | `testType.equal()` | + | `CommonKeys` | `CommonPropKeys` | + | `PadLeft` | `PadStart` | + + `isType()` itself is unchanged — only the `t`, `f`, `never` and `equal` members hanging off it are gone. + + Newly deprecated, to be removed in the next major: + + | Deprecated | Replacement | + | --- | --- | + | `Concat` | `ArrayPlus.Concat` | + | `drop()` | none — the type does not sufficiently cover the use cases | +- abb9141: Stop exposing implementation helpers as public namespace members. + + 157 helpers were reachable from the package entry, for example `IsNumber._D`, `IsBigint._SD`, + `IsIntegerLiteral._L`, `IsNever._O`, `Pick._`, `Some._Fn`, `$Fn._Test`, `$StrictOptions._Message`, + `ArrayPlus.IndexAt._`, `TuplePlus.Filter._Fn`, and every `X.$UtilOptions`. + The undocumented `Some.Strict`, `Some.StrictArray`, `Some.StrictTuple`, `Some.Loose`, `Some.LooseArray` + and `Some.LooseTuple` were public too. + They are now unexported module-level types, so they no longer show up in autocomplete and can change + without a breaking release. The public types they back behave the same. + + Migration: + + - `X._*` and `Some.Strict*`/`Some.Loose*`: use the public type (`IsNumber`, `IsNumber.$`, + `Some`) instead. + - `X.$UtilOptions`: spell out the options it stood for. For `Assignable` and `NotAssignable` that is + `$Selection.Options & $Distributive.Options`; the predicates that take `exact` add `& $Exact.Options`. + - `$StrictOptions._Message`: read the message off the constraint, + `$StrictOptions<{ [k in K]: unknown }, A>[K]`. +- f1fef6d: Merge `Equal` into `IsEqual`, move `IsLiteral` and `Some` onto `$Options`, + and remove the last deprecated positional aliases (#707). + + **Behavior change: `IsEqual` no longer treats symbols as equal to each other.** + The old `IsEqual` fell back to "both are symbols, so equal" whenever the structural check failed. + It now uses `Equal`'s implementation, which has no such fallback: + + | | before | after | + | --- | --- | --- | + | `IsEqual` | `true` | `true` | + | `IsEqual` (two distinct `unique symbol`s) | `true` | `false` | + | `IsEqual` | `true` | `false` | + + `testType.equal` routes through `IsEqual`, so an assertion such as + `testType.equal(true)` now fails to compile and must become `(false)`. + + `IsEqual` and `IsNotEqual` take the `$Options` object in place of positional `Then`/`Else`, + and gain `{ selection: 'filter' }`, `$Branch` and `$Fn`. + `IsNotEqual` keeps `A` when it differs from `B`, + which is the filter form #275 asked for. + + `Some`'s `Mode` moves into `$O` as `mode`, beside `selection` and the branch overrides. + + Migration: + + | Before | After | + | --- | --- | + | `Equal` | `IsEqual` | + | `Equal.$Fn`, `Equal.$Branch`, `Equal.$Same`, ... | `IsEqual.$Fn`, `IsEqual.$Branch`, `IsEqual.$Same`, ... | + | `IsEqual` | `IsEqual` | + | `IsNotEqual` | `IsNotEqual` | + | `NotEqual` | `IsNotEqual` | + | `IsLiteral` | `IsLiteral` | + | `Some` | `Some` | + | `Some` | `Some` | + | `CanAssign`, `IsAssign` | `Assignable` | + | `StrictCanAssign` | `Assignable` | + | `Extendable` | `Assignable.$` | + | `NotExtendable` | `NotAssignable.$` | + | `IsExtend` | `Assignable.$` | + | `IsNotExtend` | `NotAssignable.$` | + + `StringIncludes` and `NotUnknownOr` keep their positional parameters by design. +- 0523360: Remove `LooseArrayType`, `IsLooseArray`, `NotLooseArrayType` and `IsNotLooseArray`. + + These were a stopgap added while `ArrayType` still did a strict, tuple-excluding + check (#330). `ArrayType` and its variances are gone in 8.0.0, and their + replacement `IsArray` is loose by default — a tuple is an array, the same way a + string literal is a `string` for `IsString`. That leaves the stopgap with nothing + to stop. + + Migration: + + | Removed | Replacement | + | --- | --- | + | `LooseArrayType` | `IsArray` | + | `IsLooseArray` | `IsArray` | + | `NotLooseArrayType` | `IsNotArray` | + | `IsNotLooseArray` | `IsNotArray` | + + The filter form is not a literal drop-in: `IsArray` distributes over unions, so + `IsArray` is `number[]`, where + `LooseArrayType` returned the whole `number[] | 1`. + + For anyone who relied on the old strict `ArrayType` behaviour, that is + `IsArray` (`{ selection: 'filter', exact: true }` to filter). + + `ArrayPlus.IsReadonly` is now built on `IsArray`; its behaviour is unchanged. +- 2a0d791: Rename `case*` to `$*` to make them easier to use. +- d677a79: Remove `StrictBigintType` and `NotStrictBigintType` +- 47222b5: Make the `ArrayPlus`, `MathPlus`, `NumericPlus`, `ObjectPlus`, `StringPlus`, `TuplePlus` and `Bit` namespaces show their TSDoc on hover. + + Six of them were `export * as X` re-exports of a module. TypeScript never attaches a doc comment to such a namespace, so hovering `ArrayPlus` showed nothing. All seven are now declared namespaces whose members alias the types they group. The members are the same types as before. + + **Breaking, for `verbatimModuleSyntax` only.** A declared namespace exists only at the type level, so the package no longer exports an (empty) runtime object for `ArrayPlus`, `MathPlus`, `NumericPlus`, `ObjectPlus` or `TuplePlus`. Under `verbatimModuleSyntax`, a value import of one now fails with TS1484. `StringPlus` and `Bit` already worked this way. + + Migration: import them as types. + + ```ts + import type { ArrayPlus } from 'type-plus' + // or + import { type ArrayPlus, testType } from 'type-plus' + ``` +- 3fbd3dc: Honor `exact` on the numeric sign and integer predicates, and add the literal variants. + + `IsPositive`, `IsNegative`, `IsInteger`, `IsNotPositive`, `IsNotNegative` and `IsNotInteger` + declared `exact` and never read it. The option compiled and did nothing. It now follows the string + family, where `exact` separates the wide type from its literals: with `exact: true` only the wide + `number` and `bigint` match, and every literal answers the `$else` branch. The sign of a wide type + is unknown, so it stays `boolean`. + + ```ts + type R = IsPositive // boolean + type R = IsPositive<1, { exact: true }> // was true, now false + type R = IsInteger // true + type R = IsNotInteger<1, { exact: true }> // was false, now true + ``` + + The other half of the split is new: `IsPositiveLiteral`, `IsNegativeLiteral`, `IsIntegerLiteral`, + `IsNotPositiveLiteral`, `IsNotNegativeLiteral` and `IsNotIntegerLiteral` match only literals, the + way `IsNumberLiteral` does for `IsNumber`. + + ```ts + type R = IsPositiveLiteral<1> // true + type R = IsPositiveLiteral<1n> // true + type R = IsPositiveLiteral<-1> // false + type R = IsPositiveLiteral // false + type R = IsIntegerLiteral<1.1> // false + ``` + + They do not take `exact`. On strings `exact` excludes template literal types such as `${number}`; + numbers have no equivalent middle case — a numeric enum member and a literal intersected with a + record are both literals either way — so the option would compile and do nothing. + + For the same reason `exact` is **removed** from `IsNumberLiteral`, `IsNotNumberLiteral`, + `IsBigintLiteral`, `IsNotBigintLiteral` and `IsNumeric`, none of which ever read it. + + **Breaking** for code that: + + - passes `{ exact: true }` to `IsPositive`, `IsNegative`, `IsInteger`, `IsNotPositive`, + `IsNotNegative` or `IsNotInteger` — the option used to be ignored and now changes the answer. + Drop it to keep the old result, or reach for the matching `*Literal` type; + - passes `exact` to `IsNumberLiteral`, `IsNotNumberLiteral`, `IsBigintLiteral`, + `IsNotBigintLiteral` or `IsNumeric` — with `$StrictOptions` that is now a compile error. Drop it; + the result is unchanged. + + Calls that pass no `exact` answer exactly as before. + + Closes [#695](https://github.com/cyberuni/type-plus/issues/695). +- 3710c48: `ArrayPlus.IndexAt` now takes an options object instead of positional fallback type parameters. + + The three positional parameters `Fail`, `Upper`, and `Lower` are replaced by + `Options`, which covers five cases: + + | Option | Applies when | Default | + | --- | --- | --- | + | `$never` | `A` is `never` | `never` | + | `$array` | `A` is an array (not a tuple) | `N` | + | `caseEmptyTuple` | `A` is `[]` | `never` | + | `caseUpperBound` | `N` is past the upper bound | `A['length']` | + | `caseLowerBound` | `N` is past the lower bound | `0` | + + The `$never` and `$array` cases are new: they were previously not customizable. + Only the cases you specify are overridden; the rest keep their defaults. + + Migration: + + ```ts + // before + type R = IndexAt<[1], 1, 'f', 'u', 'l'> + // after + type R = IndexAt<[1], 1, { caseEmptyTuple: 'f'; caseUpperBound: 'u'; caseLowerBound: 'l' }> + ``` + + Note that the old `Fail` parameter also covered the `A is never` case. + If you relied on that, set `$never` to the same type as `caseEmptyTuple`. + + Default behavior is unchanged, so `IndexAt` and `ArrayPlus.At` / + `ArrayPlus.IsIndexOutOfBound` are not affected. +- 3889802: Move `ArrayPlus.IsReadonly`, `IsEmptyObject`, `IsRecord` and `IsDisjoint` onto `$O` (#720). + + Each now takes `$O extends $StrictOptions<$O, X.$Options> = {}` and exports `$Options`, `$Default`, + `$Branch` and `$Fn`, so it gains `$then`/`$else`, `{ selection: 'filter' }` and the `$Then`/`$Else` + branch selectors. `IsDisjoint.$Fn` fixes `B` and takes `A` as the input, like the other two-input + predicates. The default call form of `IsEmptyObject`, `IsRecord` and `IsReadonly` gives the same result + as before. + + **Breaking: `ArrayPlus.IsReadonly` drops its legacy options.** + `IsReadonly.Options` and `IsReadonly.DefaultOptions` are replaced by `IsReadonly.$Options` and + `IsReadonly.$Default`. The `$notArray` branch is removed: a value that is not an array now resolves to + `$else`, and passing `$notArray` is a compile error. The `$never` branch stays, and `$any`, `$unknown` + and `$void` are added. + + **Behavior change: `IsDisjoint` now always returns `true` or `false`.** + It checks `keyof A & keyof B` for shared keys, which fixes two wrong results: + + | | before | after | + | --- | --- | --- | + | `IsDisjoint<{ a: 1; c: 1 }, { a: 1; b: 1 }>` (each side has a key the other lacks) | `boolean` | `false` | + | `IsDisjoint<{ a: 1 }, {}>` | `never` | `true` | + + `ANotB`, `KeysWithDiffType`, `LeftJoin` and `Merge` only test `IsDisjoint extends true`, so + their results do not change. + + Migration: + + | Before | After | + | --- | --- | + | `IsReadonly` | `IsReadonly` | + | `IsReadonly.DefaultOptions` | `IsReadonly.$Default` | + | `IsReadonly` | `IsArray; $else: X }>` | + | `IsReadonly` | `IsReadonly` | +- 30f35be: Remove `IsStrictFunction` and `IsNotStrictFunction`. Use `IsFunction` and `IsNotFunction` instead; they give the same result for every option. + + `IsFunction` and `IsNotFunction` now honor `exact`. With `{ exact: true }` they match only the type `Function` itself, not a function signature. Before, `IsFunction` accepted `exact` and ignored it, and `IsNotFunction` rejected it. `IsFunction<() => void, { exact: true }>` is now `false`; it was `true`. + + `testType.strictFunction` still works and is now built on `IsFunction` with `exact: true`. Passing `exact: false` to it does not loosen the check. + + Every predicate with `$Options` now exports a `$Default` that spells out the value each option takes when it is left out. `IsFalse`, `IsBoolean`, `IsBigintLiteral`, `IsObject` and 46 others gained one; `IsNotFunction.$Default` now includes `$Exact.Default`. +- d0a868b: The type predicates distribute over a union by default. + + A 7.x predicate checked a union as a whole, so a union with any member that failed answered `false`. + A v8 predicate checks each member and combines the answers. A union whose members pass and fail now answers `boolean`: + + ```ts + type R = IsString<'a' | 1> // 7.x: false, 8.0: boolean + type R = IsTrue // 7.x: false, 8.0: boolean + type R = IsNotString<'a' | 1> // 7.x: true, 8.0: boolean + ``` + + The filter form keeps the members that pass. `IsString<'a' | 1, { selection: 'filter' }>` is `'a'`, where the 7.x `StringType<'a' | 1>` was `never`. + + This applies to `IsArray`, `IsBigint`, `IsBoolean`, `IsFalse`, `IsFunction`, `IsInteger`, `IsNull`, `IsNumber`, `IsNumeric`, `IsObject`, `IsString`, `IsSymbol`, `IsTrue`, `IsTuple`, `IsUndefined`, `IsVoid`, and the `IsNot*` negation of each. + `IsPositive`, `IsNegative`, `IsNotPositive` and `IsNotNegative` already distributed in 7.x and do not change. + + A result tested with `extends true` behaves as before, because `boolean` does not extend `true`. + A result tested with `extends false`, or compared with `IsEqual`, changes. + + Migration: pass `{ distributive: false }` to keep the 7.x result. + + ```ts + type R = IsString<'a' | 1, { distributive: false }> // false + type R = IsString<'a' | 1, { distributive: false; selection: 'filter' }> // never + ``` + + See [Predicates distribute over unions](https://cyberuni.github.io/type-plus/guides/migrating-to-v8/#predicates-distribute-over-unions) in the migration guide. +- 3ea3bb4: Remove the top-level `Partial`, `Required`, `Pick` and `Omit` (#740). + + **Breaking.** They shadowed the TypeScript built-ins of the same name while meaning something else, + and were deprecated aliases of the `ObjectPlus` forms. Replace each one: + + | Removed | Replacement | + | --- | --- | + | `Partial` | `ObjectPlus.Partial` | + | `Required` | `ObjectPlus.Required` | + | `Pick` | `ObjectPlus.Pick` | + | `Omit` | `ObjectPlus.Omit` | + + Import the namespace with `import type { ObjectPlus } from 'type-plus'`. + + Do not just delete the import: the file then compiles against the built-in, which differs in the + cases the `ObjectPlus` TSDoc lists. `Required` also strips `undefined` from required properties, + `Pick` and `Omit` distribute over a union `T`, and `Partial` adds `| undefined` under + `exactOptionalPropertyTypes`. +- a0dfb87: Remove the deprecated exports before 8.0 GA (#722). + + **Breaking.** Each name below is gone from the package. Replace it as shown: + + | Removed | Replacement | + | --- | --- | + | `Concat`, `ArrayPlus.Concat` | the spread tuple `[...A, ...B]` | + | `KeepMatch` | `Filter` | + | `MapToProp` | `IntersectOfProps` | + | `PropUnion` | `UnionOfProps` | + | `PartialExcept` | `PartialOmit` | + | `Except` | `ObjectPlus.Omit` | + | `KeysOfOptional` | `OptionalKeys` for the optional keys, `keyof T` for the key union | + | `PromiseValue

` | the built-in `Awaited

` | + | `EitherAnd` | `EitherOrBoth` | + | `NoInfer` | TypeScript's built-in `NoInfer` (TS 5.4+) | + | `Failed`, `FailedT` | `$Error` | + | `B` namespace | `Bit` (`B.And` → `Bit.And`) | + | `NumberPlus` namespace | `NumericPlus`, plus the top-level `IsNumber` and `IsNotNumber` | + | `reduceKey()` | `reduceByKey()` | + | `unpartial()` | import `unpartial` from the `unpartial` package | + | `assertType()` and its members | `testType` for type-level checks, `x satisfies T` for compile-time assignability, `isType()` or a type guard for runtime narrowing | + | `isConstructor()` | none; it passed any function callable with `new` | + | `drop()` | none; the `DropMatch` type stays | + + `testType.Failed` is a different type and stays. + + `required()` and `requiredDeep()`, also re-exported from the `unpartial` package, stay. They now have + TSDoc and a spec: `required()` merges shallowly and lets a later `undefined` overwrite, while + `requiredDeep()` merges nested objects and keeps the earlier value over a later `undefined`. +- dce951c: Remove `canAssign()` and the one-argument `isType(v)`. Neither checked anything at runtime. + `testType` is the testing API for types; in code, use `satisfies`. + + | Removed | In a test | In code | + | --- | --- | --- | + | `canAssign()(v)` | `testType.canAssign(true)` | `v satisfies T` | + | `canAssign(false)(v)` | `testType.canAssign(false)` | | + | `isType(v)` | `testType.canAssign(true)` | `v satisfies T` | + + `isType(subject, validator)` is a runtime type guard and stays. +- d6216bd: Remove `$SelectInvert` and `$SelectInvertStrict`, and export `$Void`. + + `$SelectInvert` and `$SelectInvertStrict` were an older generation of the options convention. + `IsNotNumeric`, `IsNotFunction` and `IsNotStrictFunction` now build on `$Special` and + `NotAssignable.$`, like the other `IsNot*` predicates. Their results do not change. + `IsNotNumeric.$` and `IsNotFunction.$` are new type utils that skip the special-type checks. + `IsNotNumeric.$Branch` and `IsNotFunction.$Branch` now take an optional `$O`, like their siblings. + + **Breaking** only for code that imports `$SelectInvert` or `$SelectInvertStrict`. + Use `NotAssignable.$` inside `$Special` instead, as `IsNotNumeric` does. + + `$Void` is now exported. It already appeared in public constraints such as `IsObject.$Options`, + but consumers could not name it. + + The docs now agree with the code on the default: `$O` defaults to `{}`, so a plain call such as + `IsObject` returns `true` or `false`. Pass `X.$Branch` when you compose predicates. The + `$Selection` docs no longer call `$Branch` the recommended default. Examples that used the removed + `$SelectionBranch` and `$SelectionPredicate` names now use `X.$Branch` and `$Selection.Branch`. + `IsAnyOrNever` now defaults `$O` to `{}` like the other predicates, instead of + `$Selection.Predicate`. Plain calls return the same `true`/`false`. The change also fixes + `{ selection: 'filter' }`: `IsAnyOrNever` now returns `never`. + Its examples now show the real `true`/`false` results. + + Closes [#688](https://github.com/cyberuni/type-plus/issues/688). +- 9e51d0b: Remove `NonUndefined`. Use `Exclude` instead. + Remove `NonNull`. Use `Exclude` instead. +- 0616400: Update and release as ESM package only. +- fa3708a: Reject unknown option keys. + + Every type that takes an options parameter `$O` now constrains it with `$StrictOptions<$O, X.$Options>`. + A misspelled key used to compile whenever a valid key sat next to it. It is now an error that names the key and, for a prefix match, suggests the valid one: + + ```ts + type R = IsObject<{}, { distributive: false; exactt: true }> + // Type 'true' is not assignable to type '"'exactt' is not a valid option. Did you mean 'exact'?"'. + + type R = IsNever<1, { distributive: false; $else: 'E' }> + // Type 'false' is not assignable to type '"'distributive' is not a valid option"'. + ``` + + `IsNever`, `IsNotNever`, `IsUnknown`, `IsNotUnknown`, `IsNotFunction`, `IsNotNumeric` and `IsNotStrictFunction` already resolved a `$void` branch, + but their `$Options` did not declare it. They declare `$void` now, so `IsNever` stays accepted and answers `'V'`. + + `testType` rejects unknown keys the same way. + + New exports: + + - `$StrictOptions<$O, A>`: the constraint, for your own types. + - `$ForwardOptions<$O, A, K>`: narrows a generic wrapper's `$O` to the keys `A` accepts, so the wrapper can pass it on. + - `$ErrorMessage`: the message string type the constraint reports through. + + **Breaking** for a generic type that passes its own `$O` to a type-plus type: + + ```ts + type Mine = IsObject + // error TS2344: Type '$O' does not satisfy the constraint '$StrictOptions<$O, $Options>'. + ``` + + Repeat the strict constraint when your type takes the same options, + or forward through `$ForwardOptions` when it takes its own: + + ```ts + type Mine = {}> = IsObject + + type Yours = {}> = + IsObject> + ``` + + Do not narrow with `$O extends $StrictOptions<$O, IsObject.$Options> ? IsObject : never` in a type with options of its own: + it answers `never` whenever one of those options is passed. + + The constraint costs about 3 type instantiations per use without options and about 15 with options. + See the [migration guide](https://cyberuni.github.io/type-plus/guides/strict-options/). + + Refs [#689](https://github.com/cyberuni/type-plus/issues/689). + Closes [#685](https://github.com/cyberuni/type-plus/issues/685). +- 25b5d31: Remove `Positive`, `Negative`, `NumericType`, `NotNumericType`, +- 39b0941: Move the last positional `Then`/`Else` types onto the `$Options` object (#665). + + `If`, `HasKey`, `IsOptionalKey`, `IsUnion`, `ArrayPlus.IsIndexOutOfBound` and + `StringPlus.Includes` were the remaining branching types taking their two + outcomes as positional type parameters. They now take `$O`, like the rest of + the `IsXXX` family, which also gives them `{ selection: 'filter' }` and the + `$Branch` selectors for free. + + Following #661, the positional parameters are replaced in place rather than + kept alongside the options object: position 3 cannot hold either a `Then` or an + options object without guessing which one a caller meant. + + Migration: + + | Before | After | + | --- | --- | + | `If` | `If` | + | `HasKey` | `HasKey` | + | `IsOptionalKey` | `IsOptionalKey` | + | `IsUnion` | `IsUnion` | + | `UnionType` | `IsUnion` | + | `UnionType` | `IsUnion` | + | `IsIndexOutOfBound` | `IsIndexOutOfBound` | + | `StringPlus.Includes` | `StringPlus.Includes` | + + `UnionType` is removed rather than migrated. It defaulted to + `Then = T, Else = never`, which is filter semantics, and `$O` spells that + natively — so the `UnionType` / `IsUnion` pair collapses into one type plus an + option, the same collapse #659 applied to the `LooseArrayType` family. + + The default call form of every type is unchanged: `IsUnion` is still + `true`/`false`, `IsIndexOutOfBound` is still `true`/`false`. + + The filter subject is the value being checked, which for the two key + predicates is the key rather than the record. That makes the filter form a key + selection: `IsOptionalKey` is + `OptionalKeys`. + + `StringIncludes` keeps its positional `Then`/`Else`. It is the low-level string + check that `StringPlus.Includes` is built on, and was not part of #665's sweep. +- 7e2ab39: Update `IsTuple` and variances. +- 322a33d: Update `IsString` and its variances. +- c130e2e: Give the transforms the options convention the predicates use. + + Every transform that took options or a positional fallback now takes one last type parameter + `$O extends $StrictOptions<$O, X.$Options> = {}`. + Its namespace declares the keys in `X.$Options` and their defaults in `X.$Default`, + and an unknown key is an error that names the key. + A `$`-prefixed key names what the type returns in one case. + The convention is written up in the [Options reference](https://cyberuni.github.io/type-plus/reference/options/). + + Migration: + + | Before | After | + | --- | --- | + | `Head`, same for `Last`, `DropFirst`, `DropLast` | `Head` | + | `IndexAt` | `IndexAt` | + | `Add`, and the same third parameter on `Subtract`, `Multiply`, `GreaterThan`, `Max` | `Add` | + | `Abs`, `StringToNumber`, `StringToBigint`, `StringToNumeric` | `Abs` | + | `At`, `ArrayPlus.At` | `At` | + | `CreateTuple` | `CreateTuple` | + | `Exclude` | `Exclude` | + | `NotUnknownOr` | `NotUnknownOr` | + | `ObjectPlus.Merge` | `ObjectPlus.Merge`; the parameter was never honored | + | `TypePlusOptions.NotArray` | removed; declare `$notArray?: unknown` on your own `$Options` | + | `X.Options`, `X.DefaultOptions` on `Head`, `Last`, `FindFirst`, `DropFirst`, `DropLast`, `CommonPropKeys` (top-level, `ArrayPlus`, `TuplePlus`), `Box`, `ArrayPlus.Find`, `ArrayPlus.Filter`, `ArrayPlus.IndexAt`, `ArrayPlus.ElementMatch`, `TuplePlus.Find` | `X.$Options`, `X.$Default` | + + The `$never`, `$array`, `$tuple`, `$notArray`, `$notBoxable`, `$notMatch`, `$widen`, `$unionNotMatch` and `widen` keys keep their names. + + `StringIncludes` keeps its positional `Then` and `Else`; `StringPlus.Includes` is the one with options. + + Two fixes ride along: + the top-level `CommonPropKeys` now honors `$never` for an array input, and + `StringToNumber` passes `$fail` through when it strips trailing zeros. + + A new `$Fail` namespace (`$Fail.$Options`, `$Fail.$Default`) gives a custom transform the `$fail` key. + +### Minor Changes + +- a8f999f: Add `Serialized` and `Deserialized`, which carry a value's type through a string so a two-line wrapper gives `JSON` or flatted a typed round trip. + + `Serialized` tags a format that keeps circular references, such as flatted's, and does not mix with plain JSON either way. +- 83b78ad: Add `ArrayPlus.Join`, the type-level `Array.prototype.join`. It joins a tuple into a string and is the inverse of `StringPlus.Split`. + + ```ts + type R = ArrayPlus.Join<['a', 'b', 'c']> // 'a,b,c' + type R = ArrayPlus.Join<['a', 'b', 'c'], '/'> // 'a/b/c' + ``` +- 5a7f0b6: Add `assertType(subject, validator, message?)`, the throwing counterpart of `isType()` for pre- and post-conditions. + It throws a `TypeError` unless `validator` passes, and narrows `subject` to `T` after the call. + + ```ts + assertType(value, (v) => typeof v === 'string', 'value must be a string') + value // string + ``` + + The validator is required. Unlike the `assertType` removed earlier in 8.0, it has no no-validator overload and no `isX` members. +- e707593: Add `ObjectPlus.Partial`, `ObjectPlus.Required`, `ObjectPlus.Pick` and `ObjectPlus.Omit`, which replace + the top-level `Partial`, `Required`, `Pick` and `Omit` (#719). The top-level names are removed in 8.0.0 + (#740). + + The top-level names shadow the TypeScript built-ins of the same name, but they mean something else. + An editor's auto-import can bring one in, and from then on every use of that name in the file + switches meaning without an error: + + - `Required` also strips `undefined` from properties that were already required: + `{ b: string | undefined }` becomes `{ b: string }`. The built-in only removes the `?`. + - `Pick` and `Omit` distribute over a union `T` and accept the keys of any member. The built-ins work + on the keys every member shares, so `Omit<{ k: 'x'; x: 1 } | { k: 'y'; y: 2 }, 'k'>` is + `{ x: 1 } | { y: 2 }` here and `{}` with the built-in. + - `Partial` adds `| undefined` to each property. It differs from the built-in only under + `exactOptionalPropertyTypes`. + + In the `ObjectPlus` namespace they no longer shadow anything. Each type's TSDoc names its + difference from the built-in. + + `Exclude` keeps its top-level name. With two arguments it is identical to the built-in, so shadowing + it changes nothing; `$O['$excluded']` only adds behavior. + + Migration: + + | Before | After | + | --- | --- | + | `import type { Pick } from 'type-plus'` | `import type { ObjectPlus } from 'type-plus'`, then `ObjectPlus.Pick` | + | `Omit` from `type-plus` | `ObjectPlus.Omit` | + | `Partial` from `type-plus` | `ObjectPlus.Partial` | + | `Required` from `type-plus` | `ObjectPlus.Required` | + + Do not just delete the import: the file then compiles against the built-in, which gives different + results in the cases above. +- 2a0189e: Add `Divide`, `A / B` on `number` and `bigint` literals, also available as `MathPlus.Divide`. + + A result that does not terminate is truncated toward zero to `precision` fractional digits, 16 by default: `Divide<1, 3>` is `0.3333333333333333`, the same literal as the runtime `1 / 3`. + Set another precision with `Divide<2, 3, { precision: 2 }>` (`0.66`). + When either input is a `bigint`, `Divide` is `Quotient`, since a `bigint` has no fractional values: `Divide<7n, 2n>` is `3n`. + A zero divisor resolves to `$fail` (`never` by default), as in `Quotient`. +- 335b870: Rename `$ExactOptions` to `$Exact.Options`, + `$ExactDefault` to `$Exact.Default`, + `$IsExact` to `$Exact.Parse` +- 4b2b89c: Replace `Equal` with the new `$Equal` type. +- d418300: Rename `$IsDistributive` to `$Distributive.Parse`. +- 1ce1275: Accept a type function (`$Fn`) as the criteria of `FindLast` (and `ArrayPlus.FindLast`), as `FindFirst` and `Find` already do. + + With a type function, a union entry is matched one member at a time, and for an array the matching element types come back with `| undefined`: + + ```ts + type R = FindLast<[1, 'x', { a: 1 }, 2], IsObject.$Fn> // { a: 1 } + type R = FindLast, IsObject.$Fn> // { a: 1 } | undefined + ``` + + This gives `FindLast` the strict mode that every other type taking a `Criteria` now has through `Equal.$Fn`, which matches an entry exactly and does not widen (#298): + + ```ts + type R = FindLast<[1, 2, 3], number> // 3 + type R = FindLast<[1, 2, 3], Equal.$Fn> // never + ``` +- 1debfc2: `$Equality` to `$Equal`. +- 407b3d3: Deprecate `NoInfer`. +- 3dc7533: Add `IsNotAnyOrNever`, the negation of `IsAnyOrNever` (#749): + + ```ts + type R = IsNotAnyOrNever // false + type R = IsNotAnyOrNever // false + type R = IsNotAnyOrNever<1> // true + ``` +- 3d70869: Remove `$Override`. It is not needed. +- abdc5fe: Update bit types +- 02b7ab7: Rename `JSONTypes`, `JSONPrimitive`, `JSONObject` and `JSONArray` to `JsonTypes`, `JsonPrimitive`, `JsonObject` and `JsonArray`. + + `type-plus` title-cases acronyms (`IsBigint`, `StringToBigint`), and the JSON types were the exception. + The old names remain as deprecated aliases of the same types and will be removed in 9.0. + + Migration: replace `JSONTypes` with `JsonTypes`, and so on for the other three. +- 53f6c0b: Consolidate `$Selection` types. +- 1c7bab7: Deprecate `Failed` and `FailedT`. +- 33c0208: Update `IsFunction`, `IsNotFunction`, `IsStrictFunction`, `IsNotStrictFunction`. + + Remove `FunctionType`, `NotFunctionType`, `StrictFunctionType`, `NotStrictFunctionType`. +- f982121: Update to require typescript 5.4. +- afac18a: Add `IsStrictObject` type. +- f4939f9: Add `testType.defer` and `testType.assert` for deferred type testing. + + A `testType.*` check asserts immediately: the expectation is an argument, so + the failure is reported where the check is written. That makes a check + impossible to extract into a reusable helper — the helper body is checked once, + against type parameters that are not yet resolved, so nothing there can pass or + fail. + + `testType.defer.*` takes no argument and returns the result as a type instead. + A helper returns the results it collected, and `testType.assert()` checks them + at the call site, where the type parameters are resolved: + + ```ts + function testMyType() { + return [testType.defer.string(), testType.defer.not.never()] + } + + it('blah', () => { testType.assert(testMyType<'a'>()) }) // passes + it('bruh', () => { testType.assert(testMyType<1>()) }) // fails here + ``` + + `testType.assert()` accepts results in any shape a helper returns — a single + result, an array, an object, or any nesting of those. `testType.defer.not.*` is + the deferred form of passing `false`. Deferred checks take the same options as + their immediate counterparts, in the same position and merged over the same + defaults. Every check has a deferred form except `inspect`, which is a + development aid rather than a check. + + A failing check resolves to `testType.Failed` rather + than a bare `false`, so the compiler error names the check that failed and the + types it compared. +- f52a747: Add `LessThan`, `GreaterThanOrEqual`, `LessThanOrEqual` and `Min`, and the matching `MathPlus` members. + + They compare `number` literals and share the limits of `GreaterThan`: `bigint`, the wide `number`, and a fractional pair whose difference is a whole number resolve to `$fail` (`never` by default). + `GreaterThanOrEqual` and `LessThanOrEqual` return that `$fail` value as is, not negated, and are `true` for two identical `number` literals, including fractional ones such as `GreaterThanOrEqual<1.5, 1.5>`. + + There is no `MathPlus.ToPositive`: `Abs` is that type. +- 2ed6063: Update `$Type` to work with primitive types and improve object type usage. +- af5a29a: Tidy the namespace surface. Nothing is removed; the losing names are deprecated and will go in a + later major. + + - `Bit` is the one name for the bit namespace. `B` is deprecated: it is the name type parameters are + most often given (`And`), so a type parameter `B` shadows the namespace inside any generic that + declares one. Replace `B.And` with `Bit.And`. + - `NumberPlus` is deprecated in favor of `NumericPlus`. It held the same members plus `IsNumber` and + `IsNotNumber`, which are on the top level. Replace `NumberPlus.IsInteger` with + `NumericPlus.IsInteger`, and `NumberPlus.IsNumber` with `IsNumber`. + - `MathPlus` now holds every math type: `Abs`, `GreaterThan` and `Max` join `Add`, `Decrement`, + `Increment`, `Multiply`, `Subtract` and `ToNegative`. + - `KeepMatch`, `PropUnion` and `MapToProp` are deprecated. Use `Filter`, `UnionOfProps` and + `IntersectOfProps`, the same types under their primary names. + - The `ArrayPlus` and `TuplePlus` TSDoc now lists what each namespace exports. +- 9e7d076: Add the missing negations of the predicates: + `IsNotUnion`, `IsNotLiteral`, `IsNotOptionalKey`, `IsNotEmptyObject`, `IsNotRecord`, + `HasNoKey`, `HasNoNull`, `HasNoUndefined` and `HasNoVoid`. + + Each one inverts its positive form, keeps the same rules, and has the same `$Options`, `$Default`, `$Branch` and `.$Fn` surface + (`IsNotLiteral` has no `.$Fn`, because `IsLiteral` has none). + The filter form keeps the input that fails the positive check: + + ```ts + type R = IsNotUnion // number + type R = HasNoNull<1 | 2, { selection: 'filter' }> // 1 | 2 + type R = HasNoKey<{ a: 1; b: 2 }, 'a' | 'c', { selection: 'filter' }> // 'c' + ``` + + For a predicate without a counterpart, such as `IsDisjoint`, negate its type function with `$Fn.Not`. + + This change is not breaking: it only adds exports. +- b98ed6d: Accept `readonly` for array. + Add `ArrayPlus.IsReadonly`. + + Fix `ArrayPlus.Reverse` to support `readonly`. +- 19663d7: Rename `$SpecialType` to `$Special`. +- f09ef4d: Add `$Void` support. +- 5f1ff23: Deprecate `NotExtendable`, `IsExtend` and `IsNotExtend`, completing the group + `Extendable` already belonged to. Nothing is removed and no behavior changes; + each type now names its replacement. + + | Deprecated | Use instead | + | --- | --- | + | `IsExtend` | `Assignable.$` | + | `IsNotExtend` | `NotAssignable.$` | + | `Extendable` | `Assignable.$` | + | `NotExtendable` | `NotAssignable.$` | + + `#665` names the replacement as `$Assignable`, which does not exist. The `$` + type util hanging off `Assignable` and `NotAssignable` is the equivalent, and + the mapping above is pinned by compiled assertions in + `src/predicates/predicates_docs.spec.ts` rather than asserted. + + ⚠️ Migrating to plain `Assignable`/`NotAssignable` instead of the `$` member is + **not** a rename. The plain types special-case `any`, `never` and `unknown`, so + three inputs change answer: + + ```ts + IsExtend // boolean + Assignable // true + Assignable.$ // boolean -- the equivalent + ``` + + `Assignable.$` also requires its options argument explicitly (`{}` at minimum), + and supports `{ distributive: false }`, which the deprecated four cannot + express. +- 23ecd4a: Support TypeScript 5.4 and up. + + The `typescript` peer range widens from `>= 5.6.0` to `>= 5.4.0`. Nothing in + the published types ever required 5.6 — the range was simply narrower than + what the compiler matrix tested. `verify:dts` now type-checks the emitted + `esm/index.d.ts` with 5.4, 5.5, 5.6 and 6.0, so the range is checked rather + than asserted. + + The package is also now built with TypeScript 7 — one build, as before. + Emitted JavaScript is unchanged; the declarations differ only in how the + compiler renders them. +- cf966e7: Add `.$Fn` to every one-input predicate, so each of them can be passed to `Filter`, `Find`, `Some` and `DropMatch`. + + The special-type, primitive, numeric and structure predicates (`IsAny` … `IsUnion`), `HasNull`, `HasUndefined`, `HasVoid`, and every `IsNot…` counterpart, including `IsNotObject`, now expose `.$Fn<$O>`, the same shape as `IsObject.$Fn`: + + ```ts + type R = TuplePlus.Filter<[1, 'a', 2.5], IsInteger.$Fn> // [1] + type R = $Fn.Apply // true + ``` + + Prefer `IsNotX.$Fn` over `$Fn.Not`: it costs about 110 fewer instantiations per `Filter` call on a 10-entry tuple (TypeScript 6.0). +- 75a74e0: Add `If.$Fn` and `ArrayPlus.IsIndexOutOfBound.$Fn`, so both can be passed to `Filter`, `Find`, `Some` and `DropMatch`. + + `If.$Fn` sends an input that is not a `boolean` to `$else`. + `ArrayPlus.IsIndexOutOfBound.$Fn` fixes the array and takes the index as its input; an input that is not a `number` goes to `$else`. +- 2d4d9b4: Reintroduce CJS support. +- fe284e2: Add `Quotient` and `Remainder`, integer division on `number` and `bigint` literals, also available as `MathPlus.Quotient` and `MathPlus.Remainder`. + + `Quotient<-7, 2>` is `-3`: it truncates toward zero, as `bigint` division does at runtime. + `Remainder<-7, 2>` is `-1`: it takes the sign of the dividend, as `%` does. + A fractional input or a zero divisor resolves to `$fail` (`never` by default). +- 69c5409: Add `Merge` and `ObjectPlus.Merge` +- df9c7fc: Align `RequiredPick` and `RequiredExcept` with `PartialPick` and `PartialOmit` (#739). + + - Add `RequiredOmit`, and deprecate `RequiredExcept` as an alias of it. `Omit` names the + complement, as in `PartialOmit`. + - `RequiredPick` and `RequiredOmit` now constrain `U` to `UnionKeys` instead of `keyof T`, so `U` + may name a key of any member of a union `T`. + - Both now distribute over a union `T`. This changes the result for a union input: + + ```ts + type U = { k: 'x'; a?: 1 } | { k: 'y'; a?: 2; b?: 3 } + + type R = RequiredPick + // before: { a: 1 | 2 } & { k: 'x' | 'y' } + // after: ({ k: 'x' } & { a: 1 }) | ({ k: 'y'; b?: 3 } & { a: 2 }) + ``` + + For a non-union `T`, the result has the same properties as before. +- d5e732a: Update `Equal` signature and implementation. + Deprecate `IsEqual` and `IsNotEqual` in favor of `Equal`. +- 9c1ae45: Add `$Options`, `$Default` and `$Branch` to `HasNull`, `HasUndefined`, `HasVoid` and `IsAnyOrNever`, like the other predicates. + + Their option errors now name `$Options`, as every other predicate's do, instead of `Options`. `HasNull.$Branch`, which the `HasNull` docs already used, now exists. +- 5263e8f: Rename `$DistributiveOptions` to `$Distributive.Options` and `$DistributiveDefault` to `$Distributive.Default`. +- 17bf492: Remove `$Exact` namespace. + `$Exact.$Options` is now `$ExactOptions`. + `$Exact.$Default` is now `$ExactDefault`. + `$Exact.$IsExact` is now `$IsExact`. +- 4c991f1: Add `Assignable`. + Deprecated `CanAssign` and `StrictCanAssign`. +- 51a7bea: Rename `$Select` to `$Equal`. + Remove `$SelectStrict`, use `$Equal` instead. +- de54957: Add customize support for `ArrayPlus.Filter` +- b150471: Updato logical types. +- ed723c7: Add `$Void` support to `IsAny` and `IsNotAny`. +- e7ad7e3: Add `Slice`, also available as `ArrayPlus.Slice`: the type level `Array.prototype.slice()`. + + It gets the section of an array or tuple from `Start` up to, but not including, `End`, with negative indices and out-of-bound clamping as `slice()` does: `Slice<[1, 2, 3], -2>` is `[2, 3]`. +- 92fb5ad: Add `testType.has*` for asserting union membership. + + `testType.undefined()` asks whether `T` *is* `undefined`. There was no way + to ask whether `T` *contains* `undefined`, which is the common shape when a + value is optional: + + ```ts + type R = number | undefined + + testType.hasUndefined(true) + ``` + + Three assertions are added — `hasUndefined`, `hasNull` and `hasVoid` — backed + by the `HasUndefined`, `HasNull` and `HasVoid` predicates. `HasUndefined` + already existed; `HasNull` and `HasVoid` are new and are exported alongside it. + + All three distribute over the union explicitly, checking each branch on its + own before folding the branches back into a single answer. `HasUndefined` + keeps the results it had; the explicit distribution matters for `HasVoid`, + because `IsVoid` widens to `boolean` and would otherwise + report `number | undefined` as containing `void`. + + The `has*` methods take no options type parameter, alongside `any`, `unknown`, + `never` and `equal`: `distributive` is what the check is made of, and none of + `undefined`, `null` or `void` has a literal subtype for `exact` to narrow. Note + that `has*` is not the same as passing `{ distributive: true }` to the plain + check — distribution widens the result to `boolean`, which accepts both `true` + and `false` and so asserts nothing, while `has*` folds the branches back into a + single answer. + + Each also has a deferred form — `testType.defer.hasUndefined()` and + `testType.defer.not.hasNull()` — keeping the rule that every `testType` + check but `inspect` mirrors into `testType.defer`. + + There is no `hasAny`, `hasUnknown` or `hasNever`. A union absorbs those types, + so they can never be one branch among several, and `testType.any`, + `testType.unknown` and `testType.never` already answer the question. +- 7051adc: Add the type-level forms of more `String.prototype` methods to `StringPlus`: + + - `StringPlus.StartsWith` and `StringPlus.EndsWith`: predicates shaped like `StringPlus.Includes`, with `$then`/`$else`, `selection: 'filter'` and the `$Branch` selectors. + - `StringPlus.Replace` and `StringPlus.ReplaceAll`: replace the first or every occurrence of a string pattern. + + ```ts + type R = StringPlus.StartsWith<'abc', 'ab'> // true + type R = StringPlus.ReplaceAll<'a.b.c', '.', '/'> // 'a/b/c' + ``` +- d884894: Add `$DefineInputOptions` and `$DefineBranchOptions`. + Add support of handing `$any`, `$unknown`, `$never` for `IsAny`. + + Add `$ResolveBranch` that fixes the `unknown` pass-through issue. + + The types will be able to use in the form of `IsAny`, hopefully. + + It's still not recommended doing so, but at least it will not produce weird results. + + Will need to convert other types to support that and add tests for them. +- e5d3af3: Add `testType.callableWith` and `testType.constructibleWith`, which check that a function can be called, or a class constructed with `new`, with a list of argument types. + The check passes when one of the overloads accepts the arguments, which `equal` on `Parameters` cannot express because it keeps only the last overload. + + ```ts + function f(value: string): string + function f(value: number, radix: number): string + + testType.callableWith(true) + testType.callableWith(false) + testType.constructibleWith(true) + ``` + + Both are also available on `testType.of(value)` and `testType.defer`. +- 057081a: Name the check, the actual type and the expected type when an immediate `testType` check fails. + + `testType.equal(true)` failed with `Argument of type 'true' is not assignable to parameter of type 'false'`. + It now fails with `... parameter of type 'false | Failed<"equal", string, number>'`, the same `testType.Failed` a `testType.defer` check reports. + Checks are called the same way. The new `testType.Expectation` type is the `expected` parameter of every immediate check, including `testType.property`, `testType.callableWith`, `testType.constructibleWith` and the checks `testType.of(value)` returns. +- 8c2e329: Add `parameters`, `returns` and `resolves` to `testType.Subject`, so `testType.of(value)` can check a function's parameters, its return type, or what a promise resolves to with any `testType` check. + + ```ts + testType.of((a: number) => String(a)).parameters.equal<[a: number]>(true) + testType.of((a: number) => String(a)).returns.equal(true) + testType.of(async () => 1).returns.resolves.equal(true) + ``` + + On a subject that is not a function (or not a `PromiseLike` for `resolves`), the member is a `testType.Failed` with no checks, so a check on it fails to compile. +- 3f63493: Add `testType.of(value)`, which checks the type of a value without naming a variable and writing `typeof`. + It returns a `testType.Subject` holding every `testType` check with the subject bound to the type of `value`. + + ```ts + testType.of([1, 2].map(String)).equal(true) + testType.of('a' as const).string<{ exact: true }>(false) + ``` + + An inline literal widens as in any generic call, so write `as const` to keep it narrow. +- 58c425b: Add `testType.property(expected)`, which checks that type `T` has the key `K`. + It is `testType.true>(expected)` under a name, with a deferred form and a `testType.of(value).property()` counterpart. + + ```ts + testType.property<{ a?: 1 }, 'a'>(true) + testType.property<{ a: 1 }, 'a' | 'b'>(false) // every key in `K` must be present + testType.of({ a: 1 }).property<'a'>(true) + ``` +- c56d4a0: Change `$ResolveBranch` type parameters order. + `T` is moved to the last position as `D` (as it is the default value), and made optional. +- c87ca35: `testType` type checks accept options. + + Each check now takes an optional second type parameter carrying the behavioral + options of the `IsXXX` type behind it — `testType.$Options` is + `{ distributive?: boolean; exact?: boolean }`. `canAssign` and + `strictCanAssign` take it as their third and accept `distributive` only. + + ```ts + testType.string<'a', { exact: true }>(false) + testType.array<[string], { exact: false }>(true) + testType.string<'a' | 1, { distributive: true }>(true) + testType.strictCanAssign(true) + ``` + + Options are merged over each method's own defaults, so the existing + no-options call form keeps the behavior it has always had. The parameter + defaults to `{}` and sits after the type under test, so inference at existing + call sites is unchanged — the whole suite type-checks unmodified on TypeScript + 5.4 through 7. + + `any`, `unknown`, `never` and `equal` take no options — none of the types + behind them has a distributive or exact dimension. A new guide, + [Migrating from Then/Else to $Options][migration], documents how that older + signature maps onto `$O` and which types have yet to move. + + [migration]: https://cyberuni.github.io/type-plus/guides/migrating-then-else-to-options/ +- cb37ca4: Add `AdjustExactOptionalProps`. +- 7116392: Add `Box` to box primitive types to their boxed types. +- cbab0f0: Add `.$Fn` to the two-input predicates `Assignable`, `NotAssignable`, `Equal`, `HasKey` and `IsOptionalKey`. + + A type function has one input, so the entry being checked is the function's input and the other input is fixed as `.$Fn`'s first type parameter, ahead of the options: + + ```ts + type R = TuplePlus.Filter<[1, number, 1], Equal.$Fn<1>> // [1, 1] + type R = TuplePlus.Find<[string, 1], Assignable.$Fn> // 1 + type R = TuplePlus.Filter<[{ a: 1 }, {}], HasKey.$Fn<'a'>> // [{ a: 1 }] + ``` + + `Equal.$Fn` gives `Filter`, `Find` and `DropMatch` an exact match, which is what `Some`'s `'strict'` mode does. +- 4dc227a: Update `IsObject` and variants +- cca9408: Let the collection types take type-plus predicates as type functions. + + `$Fn` is a type function and `$Fn.Apply` calls it. + `TuplePlus.Filter`, `TuplePlus.Find`, `ArrayPlus.Find`, `Some`, `DropMatch` (and `TuplePlus.DropMatch`, `ArrayPlus.DropMatch`) accept either a plain type, matched with `extends` as before, or a `$Fn`, which matches when it returns `true`. + `IsObject.$Fn<$O>` is `IsObject` as a type function with its options applied, and `$Fn.Not` negates one. + + ```ts + type R = TuplePlus.Filter<[1, { a: 1 }, 'x', object], IsObject.$Fn<{ exact: true }>> // [object] + type R = TuplePlus.Filter<[1, { a: 1 }, 'x', object], $Fn.Not> // [1, 'x'] + ``` + + A plain type costs about 10 more instantiations per `Filter` call on a 10-entry tuple (TypeScript 6.0, 100 distinct inputs). + Calling a `$Fn` costs about 6 per entry on top of the predicate itself. +- b364e73: Export `$Exact`. + +### Patch Changes + +- 3d17bac: Remove the unused `tersify` dependency. + Nothing in `type-plus` imports it since the deprecated exports were removed, and it pulled in `acorn`, about 1.3 MB per install. +- 16934d0: Mark the `typescript` peer dependency as optional. + + npm 7 and later install a required peer automatically, so every install of `type-plus`, including transitive ones from packages that only use its runtime functions, pulled in TypeScript 7 and its native compiler binary, about 30 MB. + The `>= 5.4.0` range still applies when TypeScript is installed. +- 7fd4ff7: Drop the beta wording from the readme and summarize what changes from 7.x. + Correct the TSDoc examples of `IsBoolean`, `IsEqual`, `IsNull`, `IsSymbol`, `IsUndefined`, `IsVoid`, `Assignable` and `NotAssignable` that did not match what the types return. +- b95ea3b: Accept a type function (`$Fn`) in `ArrayPlus.Filter`, and so in `Filter` and `KeepMatch` on an array. + + `Filter, IsObject.$Fn>` returned `never[]`, because `ArrayPlus.Filter` compared the element type against the function with `extends`. + It now keeps the members of the element type that the function returns `true` for (`Array<{ a: 1 }>`), and returns `never[]` when the function matches none of them. +- 4ae069b: Fix `IsPositive` should return `boolean` +- 3bcc45a: Document that `NumericToString` is the inverse of `StringToNumber`, `StringToBigint` and `StringToNumeric` alike, so the casts need no `NumberToString` or `BigintToString`. + `StringToNumber` and `StringToBigint` now say which strings they reject and point to `StringToNumeric` and `NumericToString`. +- e110219: Fix `$ResolveOptions` type to handle `undefined` value. +- 973cc4a: Move source under `packages/type-plus`. +- 929fb13: Add `typescript` peer dependency (v5.6.0 or higher). +- 3ed4dce: Optimize `SplitAt` by moving never check of `DeleteCount` to the top. +- ac73d79: Removing incorrect usage of the `typesVersions` field in `package.json`. +- 5535a4a: fix `IsArray` +- e6a3001: Fix `Merge` and `ObjectPlus.Merge` dropping `readonly` when the two types are disjoint. + + `{ ...a, ...b }` copies property values onto a fresh object, so the result is always + writable. The disjoint fast path returned `A & B` unchanged, which kept `readonly` from + either side. Because a get-only accessor is a `readonly` property, that made a getter + merge in as a read-only property instead of a plain data property: + + ```ts + type R = Merge<{ get config(): ResolvedConfig }, { a: string }> + // was: { readonly config: ResolvedConfig } & { a: string } + // now: { config: ResolvedConfig; a: string } + ``` + + Closes [#598](https://github.com/unional/type-plus/issues/598). +- bbe256c: Remove `exports.default` in `package.json`. + + That provide the wrong file to systems expecting CJS. + May need to add a different one for browser-spec. +- e58fac6: Ship a `cjs/package.json` marker so `require('type-plus')` works again. + + The package root declares `"type": "module"`, so Node treated the CommonJS + output in `cjs/` as ESM. `cjs/index.js` then resolved its own `require()` + calls against the caller instead of against the package, and every CJS + consumer failed with `Cannot find module './array/array_plus.js'`. + + The build now writes `cjs/package.json` (`{"type":"commonjs"}`) and, for + symmetry, `esm/package.json` (`{"type":"module"}`) after each compile. The + 7.x build emitted the CJS marker via `buddy ts build cjs`; moving to a plain + `tsc` emit dropped it. +- 7227250: Simplify `Omit` type as the simpler code is working with typescript 5.4 +- 8901e75: Work around excessive stack depth error in TS 5.4 for type `Zeros`. +- 10f7636: Keep the `?` and `readonly` modifiers of `A` in `LeftJoin`. + + `LeftJoin<{ a?: number; b?: string }, { b: boolean }>` returned `{ a: number; b: boolean }`: the properties carried over from `A` lost their modifiers. + It now returns `{ a?: number; b: boolean }`, and a `readonly` property of `A` stays `readonly`. + On a key that both `A` and `B` declare, `B` still wins the type and the modifiers. +- af380ad: Remove the unreferenced half of the legacy `src/**/readme.md` documentation + tree, and the stale root `info.md`. + + These are v6/v7-era pages documenting an API that no longer exists — for + example `src/numeric/readme.md` still described `Positive`, `Negative`, + `Integer`, `IsWhole` and a `NumericType` signature, none + of which the package exports any more. `src` is listed in `files`, so they were + shipping in the tarball; 13 of the 33 are gone from it now. + + Nothing was lost. The explanation that only lived in these pages was ported to + the documentation site first: branch-option composition, `$Special`, `$Error` / + `$InferError` and the `$Type` brand moved onto the *type branching* page, and + `Equal` — previously undocumented on the site — gained an *Equality* page + carrying the one-level intersection caveat. + + The remaining 20 pages stay for now: `packages/type-plus/readme.md` links into + them 122 times, so they cannot be removed without rewriting the published + readme. +- 3c8a66a: Update `AdjustExactOptionalProps` to trim empty object (`{}`) when the input type has only optional or required properties. +- 377d481: Correct the TSDoc for the `string`, `object`, `tuple` and `union` types, and pin every documented + example to the implementation. + + Twenty-four documented claims did not match what the types actually resolve to: + + - `IsNotString` and `IsNotString` were documented as `false`; the special types are + not strings, so the negation accepts them and both are `true`. `IsNotString` was documented as `false` and is `true`. + - `IsStringLiteral` and `IsNotStringLiteral` illustrated the `exact` option with `'${number}'`, an + ordinary string literal, where the template literal `` `${number}` `` was meant. + - `IsObject`'s first example asserted `IsNotObject // true` — the wrong type, and the wrong + answer for it. The `{ selection: 'filter' }` examples for `IsObject`, `IsNotObject` and + `IsNotTuple` were missing the option they were demonstrating. + - `DropLast<[1, 2, 3]>` was documented as `[2, 3]`, copied from `DropFirst`; it is `[1, 2]`. + - `IsNotTuple<[] | 1, { distributive: false }>` was documented as `false` and is `true`. + - `TuplePlus.Find<[true, number | string], string>` was documented as `string | undefined`; + `$unionNotMatch` defaults to `never`, so it is `string`. The example now shows both. + - `TuplePlus.Filter`'s examples were written as bare `Filter`, which resolves to the unrelated + array `Filter`, and `UnionType`'s examples were written as `IsUnion`. + - The branch examples used names that do not exist — `$IsString.$Branch`, `$IsNotStringLiteral.$Branch` + and the retired `$SelectionBranch` — and two of them named `IsString` in `IsTemplateLiteral`'s docs. + + On the docs site, `$ExtractManipulatedString>` was documented as `'abc'`. The + intrinsic resolves before the type sees it, so the result is `'ABC'`; only an unresolved intrinsic + can be seen through. + + `src/string/string_docs.spec.ts`, `src/object/object_docs.spec.ts`, `src/tuple/tuple_docs.spec.ts` + and `src/union/union_docs.spec.ts` now pin every documented example with `testType.equal`, so an + example that drifts from the implementation fails to compile. +- d852d76: Add docs for `ArrayPlus.IsReadonly`. + + The following are internal changes thus not considered a breaking change: + + - Replace `MergeOptions`/`MergeCases` with `TypePlusOptions.Merge`. + - Rename `TypePlusOptions.Predicate` to `TypePlusOptions.Selection`. +- fd70b47: `IsNotAny`, `IsNotNever`, `IsNotUnknown`, `IsNotVoid`, `IsNotObject`, `IsNotString` and + `NotAssignable` skip the options machinery when called without options, like their positive + counterparts. Results are unchanged. + + Instantiations per use without options (TypeScript 6.0 and 7, 300 distinct inputs): + + | Type | Before | After | + | --- | ---: | ---: | + | `IsNotAny` | 29 | 10 | + | `IsNotNever` | 29 | 13 | + | `IsNotUnknown` | 29 | 12 | + | `IsNotVoid` | 68 | 23 | + | `IsNotObject` | 61 | 25 | + | `IsNotString` | 59 | 24 | + | `NotAssignable` | 67 | 39 | + + "Before" already includes the cheaper `$Special` guards from the same release. On 8.0.0-beta.11 each + type cost 1.5 to 2 times its "Before" figure. +- 29f0d2f: Document the 8 undocumented exports in `src/predicates/` and the 7 branch + markers in `src/$type/`. + + Every `@example` is pinned by a compiled assertion in + `src/predicates/predicates_docs.spec.ts` and `src/$type/$type_docs.spec.ts`. + + `$Then`, `$Else`, `$Selection`, `$Distributive` and `$Exact` are what the + `$Options`/`$Branch` convention rests on, so they now say what a caller passes + and what comes back. + + Six of the predicates are listed in #665 for removal or migration. Each says so + in its own TSDoc rather than reading as current API. +- d76aa71: Honor the special-type branch options in `IsPositive`, `IsNegative`, `IsInteger`, `IsNotPositive`, `IsNotNegative` and `IsNotInteger`. + + They declared `$any`, `$unknown`, `$never` and `$void` but ignored them. Passing one now overrides that branch: + + ```ts + type R = IsPositive // was false, now 1 + type R = IsNotInteger // was true, now 3 + ``` + + Without these options the results are unchanged. +- 2aa4e5e: `Add`, `Subtract` and `Multiply` now pass a failed input through the internal + `NumericStruct` operations instead of unwrapping each input first (#303). The + results are unchanged, including every `Fail` result and the results for union + inputs. +- ec50ea6: Document the 9 undocumented exports in `src/math/`. + + Every `@example` is pinned by a compiled assertion in + `src/math/math_docs.spec.ts`, so an example that drifts from the + implementation fails to build. + + The edges these types were missing documentation for are the ones a caller + cannot guess: + + - `GreaterThan` and `Max` accept `bigint` in their constraint but resolve to + `Fail` for every `bigint` argument. + - Arithmetic on fractional literals whose result is a whole number resolves to + an error string rather than a number. + - `Add`, `Subtract` and `Multiply` are exact decimal, so `Add<0.1, 0.2>` is + `0.3` where the runtime gives `0.30000000000000004`. + - Nothing guards overflow past `Number.MAX_SAFE_INTEGER`. +- f7e8f5c: Classify an intersection of a primitive with a record by its primitive constituent, fixing #429. + + `IsStringLiteral`, `IsTemplateLiteral`, and their negations read the kind of a string type through + `` `${T}` ``, and `IsNegative`, `IsPositive`, `IsNotNegative` and `IsNotPositive` read a sign off the + same template. TypeScript 5.1 stopped reducing an intersection inside a template + ([microsoft/TypeScript#57918](https://github.com/microsoft/TypeScript/issues/57918), still open), so + `` `${'abc' & { a: 1 }}` `` stayed unresolved and every one of those types answered as though the + string or number were not there: + + ```ts + type R = IsStringLiteral<'abc' & { a: 1 }, { exact: true }> // was false, now true + type R = IsTemplateLiteral<'abc' & { a: 1 }> // was true, now false + type R = IsNegative<-1 & { a: 1 }> // was false, now true + ``` + + The intersected members are now peeled off before the template is built, using TypeScript's + inference to an intersection target (`T extends infer U & O`) rather than the template reduction TS + withdrew. Both spellings of the members are handled — one record holding several + (`'abc' & { a: 1, b: 2 }`) and several records (`'abc' & { a: 1 } & { b: 2 }`). + + Two shapes remain unreduced, both for the same upstream reason: an intersected index signature + (`'abc' & Record`), whose key is one `String` already declares, and an intersection + nested inside a string manipulation type (`Uppercase<'' & { a: 1 }>`), which never surfaces as a + constituent of the outer type. +- c8a49d8: Document the last 30 exports with no TSDoc, and give every family a page on the + site. + + Closes #669. `pnpm docs:llms --undocumented` now reports `0 of 273`. + + The 30 fall into two kinds. Eight are the `*Plus` namespace re-exports — + `ArrayPlus`, `NumberPlus`, `NumericPlus`, `TuplePlus`, `StringPlus` and the + `B` / `Bit` pair — which are containers, so each gets one comment saying what + it groups, how it differs from the same names on the top-level surface, and + that its members carry their own docs. The other 22 are ordinary API and get + prose plus a compiled example: the JSON types, `nominalMatch`, `isPromise`, + `mapSeries`, `DropNull` / `DropNullable` / `DropUndefined`, `ToTuple`, + `as` / `asAny`, `TypePlusOptions`, `AnyConstructor`, `AnyFunction`, `ChainFn`, + `ContextBaseShape`, `ContextBuilder`, `SystemErrors` and `SystemErrorCodes`. + + The five families with no page on the site are resolved. `src/binary/` folds + into Boolean and Logical, next to the boolean operators it mirrors, and + `src/class/` folds into Function and Functional, since a class is a constructor + function. `src/nodejs/`, `src/utils/` and the root files get pages of their own: + Node.js, Utilities, and Type Sets and JSON. `llms.txt` no longer carries an + "Undocumented families" bullet. + + Checking those examples against the compiler turned up thirteen documented + claims the implementation disagrees with, corrected here rather than shipped: + + - `FindFirst<[true, number | string], string>` is `string`, not + `string | undefined`; `FindFirst, number>` is `1 | 2`, not + `1 | 2 | undefined`; `FindFirst, number>` is `number`, + not `number | undefined`. Same three for `ArrayPlus.Find`. + - The root cause of those six: `FindFirst` documented its options as + `$noMatch` and `$unionMiss` defaulting to `undefined`. The real names are + `$notMatch` and `$unionNotMatch`, and both default to `never`. + - `KeepMatch<[1, 2, 3], number>` is `[1, 2, 3]`. Nothing is dropped, all three + are numbers. + - `FindLast`'s two examples were written as `ArrayPlus.Find<...>`, a different + type that rejects tuples. The claimed results were right; the symbol was not. + - `IsNotFunction` is `true`, not + `false`, and its `$Branch` example had `$Then` and `$Else` the wrong way + round — `Function` is a function, so `IsNotFunction` takes the else branch. + - `ExtractFunction`'s example was not valid TypeScript. It is + `ExtractFunction<(() => void) & { a: 1 }>`. + - `EitherOrBoth`'s own example called it by its deprecated name, `EitherAnd`. + + `IsFunction` and `IsNotFunction` also still referred to `$SelectionBranch`, + retired in v8, in place of their own `$Branch`. Twenty-four other files still + do; they belong to families outside this change. + + Two generator fixes back the count. `export * as X from './x.js'` aliases a + *module* symbol, whose file-level doc comment TypeScript does not surface + through `getDocumentationComment`, so eight namespaces were reported + undocumented no matter what was written on them; the generator now reads that + comment. And the coverage sentence used to say "the rest have a name and a + signature only" when the rest were the `unpartial` re-exports, documented in + that package. +- 20ded29: Use `fn.apply` instead of spread. + It has better performance and function that are already bound continue to work as expected. + + ```ts + const bindFn = fn.bind(This) + + bindFn.apply(null, args) // `this` is not affected + ``` +- 1617cd3: Cut the type instantiations spent on special types. Results are unchanged. + + `$Special` detects `any`, `unknown`, `never` and `void` with cheaper checks, which saves about 28 + instantiations per use in every type built on it. `$MergeOptions` returns the defaults directly when + there are no options to merge. + + `IsAny`, `IsNever`, `IsUnknown`, `IsVoid`, `IsObject`, `IsString` and `Assignable` also skip the + options machinery when called without options. The four special-type predicates check `T` + directly. The other three answer through the new `$Special.Values`, which picks an answer by special + type the same way `$Special` does. + + Instantiations per use without options (TypeScript 6.0 and 7, 300 distinct inputs): + + | Type | Before | After | + | --- | ---: | ---: | + | `IsAny` | 52 | 10 | + | `IsNever` | 53 | 13 | + | `IsUnknown` | 52 | 12 | + | `IsVoid` | 102 | 23 | + | `IsObject` | 101 | 25 | + | `IsString` | 97 | 24 | + | `Assignable` | 123 | 39 | +- 3e4289a: The remaining predicates built on `$Special` skip the options machinery when called without options, + like `IsString` and `IsObject`. They answer through `$Special.Values`. Results are unchanged. + + This covers the `array`, `bigint`, `boolean`, `function`, `null`, `number`, `symbol`, `tuple` and + `undefined` predicates, `ArrayPlus.IsReadonly`, `IsNumeric`, the numeric literal predicates, and the + string literal and template literal predicates, each with its `IsNot*` counterpart. + + Instantiations per use without options (TypeScript 6.0, 300 distinct inputs; TypeScript 7 is within 2): + + | Type | Before | After | + | --- | ---: | ---: | + | `IsReadonly` | 40 | 26 | + | `IsArray` | 63 | 23 | + | `IsNotArray` | 63 | 23 | + | `IsBigint` | 60 | 23 | + | `IsNotBigint` | 59 | 23 | + | `IsBigintLiteral` | 58 | 36 | + | `IsNotBigintLiteral` | 60 | 38 | + | `IsBoolean` | 60 | 23 | + | `IsNotBoolean` | 59 | 23 | + | `IsTrue` | 51 | 23 | + | `IsNotTrue` | 53 | 23 | + | `IsFalse` | 51 | 23 | + | `IsNotFalse` | 53 | 23 | + | `IsFunction` | 58 | 24 | + | `IsNotFunction` | 59 | 24 | + | `IsNull` | 51 | 23 | + | `IsNotNull` | 53 | 23 | + | `IsNumber` | 58 | 23 | + | `IsNotNumber` | 58 | 23 | + | `IsNumberLiteral` | 60 | 38 | + | `IsNotNumberLiteral` | 60 | 38 | + | `IsNumeric` | 52 | 23 | + | `IsNotNumeric` | 52 | 23 | + | `IsIntegerLiteral` | 84 | 62 | + | `IsNotIntegerLiteral` | 85 | 63 | + | `IsPositiveLiteral` | 84 | 62 | + | `IsNotPositiveLiteral` | 85 | 63 | + | `IsNegativeLiteral` | 84 | 62 | + | `IsNotNegativeLiteral` | 85 | 63 | + | `IsStringLiteral` | 67 | 39 | + | `IsNotStringLiteral` | 68 | 40 | + | `IsTemplateLiteral` | 74 | 45 | + | `IsNotTemplateLiteral` | 76 | 45 | + | `IsSymbol` | 51 | 23 | + | `IsNotSymbol` | 53 | 23 | + | `IsTuple` | 51 | 24 | + | `IsNotTuple` | 52 | 24 | + | `IsUndefined` | 51 | 23 | + | `IsNotUndefined` | 53 | 23 | +- 3dc8849: Preserve `any` and `unknown` in `Properties` so they are returned unchanged instead of mapping to object types. Expand JSDoc with description and examples. +- b480c6c: Update some docs under `numerics` +- f308d82: Accept readonly arrays and tuples in `Reverse`, `DropFirst`, `DropLast`, `CommonPropKeys`, `TuplePlus.CommonPropKeys` and `MapToProp`. + + These constrained their input to mutable arrays, so an `as const` tuple failed the constraint. + `Reverse`, `DropFirst` and `DropLast` keep `readonly` on the result, as `ArrayPlus.Reverse` does: + `Reverse` is `readonly [2, 1]`, and `DropFirst` is `readonly []`. + Mutable input gives the same result as before. +- 9ed5026: Correct the `src/**/readme.md` pages, which documented a v7 API that no longer exists. + + #667 deleted 13 of the 33 legacy readme pages because nothing linked to them. The surviving 20 are + linked from `packages/type-plus/readme.md` — the npm landing page — and nothing stopped them + drifting the same way. They had: **40 of their 117 source links pointed at files deleted in the v8 + rewrite**, and the prose around them documented types that are no longer exported. + + Replaced with the types that exist, each verified against the compiler: + + - `StringType` / `StrictStringType` / `IsStrictString` and their negations → + `IsString` and `{ exact: true }`. + - `FunctionType` / `StrictFunctionType` and their negations → `IsFunction` and `{ exact: true }`. + - `ObjectType`, `SymbolType`, `TupleType`, `ArrayType`, `AnyOrNeverType` and their negations → + `IsObject`, `IsSymbol`, `IsTuple`, `IsAnyOrNever`. + - `IsStrictNumber` and `IsStrictBoolean` → the `{ exact: true }` option on `IsNumber` and + `IsBoolean`. + - The retired `$SelectionBranch` → each type's own `$Branch`, or `$Selection.Branch` for the `Has*` + family, which has no namespace of its own. + - `$NeverOptions` / `$NeverBranch` / `$NeverDefault` → the `$Never.*` members that replaced them. + + Two claims were wrong on their own terms rather than merely outdated: + `IsStrictFunction` was documented as `never`; the intersection collapses to + `Function`, so it is `true`. And `src/null/readme.md` documented a type called `IsNotnull`. + + Also fixes three broken links: `./array.find_last.tsl19` (missing `#`), `./array.concat.ts` + (the file is `array_plus.concat.ts`), and `./src/mix-types/readme.md` on the npm landing page + (the directory is `mix_types`). + + `src/readme_docs.spec.ts` now pins every example on these pages with `testType.equal`, so the same + drift fails to compile next time. +- eae3b19: Rename the `Seperator` type parameter of `StringSplit` and `StringPlus.Split` to `Separator`. Type parameter names are not part of the call signature, so existing code keeps working. +- 3d56d1a: Fix `IsInteger` should returns `boolean` +- 8957681: Correct the TSDoc for the numeric types. + + `IsNegative`, `IsPositive`, `IsNotNegative` and `IsNotPositive` + were documented as `boolean`. The special types are not numeric, so the sign checks + reject them: `IsNegative` and `IsPositive` are `false`, and their negations + are `true`. `IsInteger` and `IsNotInteger` were documented as `false` + and `true`; the wide `number` type contains both integers and non-integers, so both + resolve to `boolean`. + + The numeric predicates now document their `filter` selection, union distribution and + `$Branch` options in the same shape as the rest of the library, and the stale + `$SelectionBranch` name in the `number` and `bigint` examples is replaced by the + `$Branch` each type actually exports. + + The `number` examples for disabling union distribution used `number | 1`, which + TypeScript collapses to `number` — so they demonstrated nothing and stated the wrong + result. They now use `1 | string`, which is a real union. + + Every documented example across `numeric`, `number` and `bigint` is now asserted by + that type's own spec, so an example that drifts from the implementation fails to + compile. +- ffc6fae: Ship a generated `llms.txt`. + + `llms.txt` is the orientation layer for an agent meeting the library cold: what + `type-plus` is, the `Is*` / `$Options` / `$Branch` conventions that govern nearly + every type in it, and which families exist. Per-symbol detail stays in the + `.d.ts`, which the agent already has. + + The file is generated from the exported surface of `src/index.ts` by + `scripts/generate-llms-txt.mjs`, never hand-written, and `pnpm docs:llms:check` + fails the build when it drifts. It is listed in `files`, so it reaches + `node_modules/type-plus/llms.txt`, and it is also published to + `https://cyberuni.github.io/type-plus/llms.txt`. +- a75d2ee: Report a non-object options argument (`IsObject`) as `Type 'string' has no properties in common with type '$Options'`, instead of listing every member of `string` as an unknown option. +- cc94afd: Fix `IsNegative` should return `boolean` +- 1e5108d: Add `SubUnion` type. +- bd98d9b: Update runtime dependencies: `tersify` `^3.11.1` -> `^4.0.6` and `unpartial` `^1.0.4` -> `^1.0.7`. + + Both are runtime `dependencies` of `type-plus`, so this changes what consumers resolve. + + Two consumer-visible consequences, neither of which touches the type-level API: + + - `assertType`'s failure message renders the validator through `tersify`, and `tersify@4` + preserves the quote style of the original source instead of normalising it to double + quotes. A validator written as `subject => typeof subject === 'boolean'` now reports + `subject fails to satisfy subject => typeof subject === 'boolean'` rather than + `... === "boolean"`. Only the diagnostic string changed; the assertion behaviour did not. + - `unpartial@1.0.7` declares `engines: { node: '>= 20' }`, so the effective Node floor for + `type-plus`'s runtime dependencies is Node 20. `tersify@4` also targets ES2020 (it was + ES5 in v3). +- 3a10ccb: Retire the `*_docs.spec.ts` convention. A test belongs with the behavior it + checks, not with where the claim came from. + + #662 introduced one file per family collecting every assertion that mirrored a + TSDoc `@example`, and #672 through #674 extended it. That split the suite along + the wrong axis: `IsNumber`'s union behavior was asserted in both + `is_number.spec.ts` and `number_docs.spec.ts`, and a reader of either had no + way to know the other existed. + + All twenty of those files are dissolved into the per-symbol specs, each + assertion placed under the test that already covers that behavior. Most were + duplicates and are simply gone — 214 of them in `number` and `numeric` alone, + where the per-symbol specs already made every claim. What survived is what the + per-symbol specs were missing, and pulling it across exposed real gaps: + `Increment` and `Decrement` had no test anywhere, `Widen` had no spec file at + all, and `Abs` covered neither zero nor a floating-point input. + + `llms.txt` no longer claims that a named list of families has every example + pinned. That sentence was derived from a glob for `*_docs.spec.ts`, which only + ever proved the file existed, and there is no honest way to compute it now. + `AGENTS.md` states the rule that replaces it. +- e4a3bc0: Stop emitting `esm/package.json`, and declare `"sideEffects": false` in the + `cjs/package.json` marker. + + The package root already declares `"type": "module"`, so `esm/*.js` are ESM by + inheritance — the marker restated what was already true. It was also costing + consumers tree-shaking. Bundlers read `sideEffects` from the nearest + `package.json` describing a module, and `esm/package.json` became that file for + every ESM entry point while carrying no `sideEffects` field, so webpack fell + back to assuming side effects. Bundling an ESM consumer that imports a single + symbol produced 32,779 bytes across 21 modules with the file present and 707 + bytes across 2 modules without it. Rollup with `@rollup/plugin-node-resolve` + produced byte-identical output either way — its own static analysis drops the + unused modules regardless — so this was a webpack-class regression, not a + universal one. + + The `cjs/package.json` marker stays: without it Node reads `cjs/index.js` as + ESM and its `require()` calls resolve against the caller. It now also carries + `"sideEffects": false`, since it shadows the root field for CJS consumers the + same way. +- a483bc7: Fix TSDoc copy-paste bugs and tag drift. + + The `$` type util docs of `IsNotBigintLiteral`, `IsNotSymbol`, `NotAssignable`, `IsNotTuple`, `IsNotObject`, `IsVoid`, `IsNotVoid`, `IsNotUndefined`, `IsNotStringLiteral` and `IsNumeric` described the wrong type or dropped the negation; they now describe what each checks. + `IsTemplateLiteral`, `IsNotTemplateLiteral`, `IsAny` and the `logical` predicates now use the same `🎭 *predicate*` and `🔢 *customize*` tags as every other predicate. + + `EitherOrBoth`, `PrimitiveTypes`, `ComposableTypes` and `NonComposableTypes` now carry a tag and a pinned `@example`. +- 383e276: Fill TSDoc and `@example` gaps on public exports. + + Newly documented: `ArrayPlus.PadStart`, `stub.build` and `stub.builder`. + The `ArrayPlus.Reverse` doc comment was a plain `/*` comment and did not show up in the `.d.ts`; it now does. + `stub.build` and `stub.builder` carry their TSDoc on a merged `stub` namespace, because the `.d.ts` emit dropped it from the expando assignments. + + Added `@example`s to `At`, `Tail`, `UnionKeys`, `UnionOfValues`, `SubUnion`, `Widen`, `Partial`, `PartialPick`, `PartialOmit`, `Pick`, `Omit`, `OptionalProps`, `ExtractFunction`, `ExcludePropType`, `KeyTypes`, `AdjustExactOptionalProps`, `RecursiveIntersect`, `And`, `Or`, `Not`, `Xor`, `Brand`, `Flavor`, `StringToNumber`, `StringToBigint`, `StringToNumeric`, `NumericToString`, and the `$Any`, `$Never`, `$Unknown` and `$Void` markers, whose examples were pseudo-code before. + A `testType.equal` in each symbol's spec pins every example. + + Fixed two wrong examples: the `selection: 'filter'` example of `HasUndefined` left out the option, and one `PadStart` line said it showed an ignored `MaxLength` but showed padding. +- 37e47dc: Remove `$Exact` branch type. It is not needed anymore. +- 214a71c: Link the readme to the new [v7 to v8 migration guide](https://cyberuni.github.io/type-plus/guides/migrating-to-v8/), which lists every breaking change with a before and after example. + + Correct the `ObjectPlus.Merge` signature in `src/object/readme.md`: it takes `A` and `B` only, since its unused options parameter was removed in 8.0. +- f3ae938: Document the v8 beta status in the readme. + + v8 is in active development again on the `beta` dist-tag, and breaking changes are + expected between beta releases. The readme now says so, and tells consumers on the beta + to pin an exact version and upgrade deliberately, or to stay on the latest official + release until v8 is stable. +- 2a0189e: A whole-number result from fractional inputs is now a numeric literal. + + `Add<1.5, 2.5>` is `4`, `Subtract<1.5, 0.5>` is `1` and `Multiply<0.5, 4>` is `2`. + Each used to resolve to an error string such as `"The value '4.0' cannot be represented as bigint or number"`. + `GreaterThan`, `GreaterThanOrEqual`, `LessThan`, `LessThanOrEqual`, `Max` and `Min` now compare a fractional pair whose difference is a whole number: `GreaterThan<1.5, 2.5>` is `false` instead of `never`. +- 46e6185: Document the 36 undocumented exports in `src/object/`. + + Every `@example` is pinned by a compiled assertion in + `src/object/object_docs.spec.ts`, so an example that drifts from the + implementation fails to build. + + Four of the newly documented types do not do what their names say. The + documentation records the behavior rather than changing it: + + - `KnownKeys` resolves to `never` for every `T` on every supported + TypeScript version. + - `KeysOfOptional` returns `keyof T` when every property is required, and + `never` as soon as one is optional. + - `RecursiveRequired` does not descend into optional properties. + - `hasKey()` and `hasProperty()` test truthiness rather than key presence, and + `getField()` replaces any falsy value with the default. + ## 8.0.0-beta.12 ### Major Changes diff --git a/packages/type-plus/package.json b/packages/type-plus/package.json index 2fad7b89e1..f927e786b0 100644 --- a/packages/type-plus/package.json +++ b/packages/type-plus/package.json @@ -1,6 +1,6 @@ { "name": "type-plus", - "version": "8.0.0-beta.12", + "version": "8.0.0", "description": "Provides additional types for TypeScript.", "keywords": [ "assert",