diff --git a/CHANGELOG.md b/CHANGELOG.md index 278efb6..fa2779d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- export the matcher builder types from the entry point — - `PrimitiveUnionMatcher` / `PrimitiveUnionMatcherW`, `TaggedUnionMatcher` / - `TaggedUnionMatcherW`, and the tagged-union factory types — and add TSDoc to - every public symbol +- add TSDoc to the four public matcher factories - document the public API in the README ## [0.8.1] - 2026-09-23 diff --git a/README.md b/README.md index 475c998..ed02e3f 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,17 @@ for the design decisions and [Caveats](#caveats) for the limits. ## API -The package exports four factories and the builder types they return. Each -factory comes in a _strict_ variant (one common return type `R`) and a -_widening_ variant: the `W` suffix means **widening** — the return value is -widened from one common `R` to the union of every handler's return type. +The package exports four factories. Each comes in a _strict_ variant (one common +return type `R`) and a _widening_ variant: the `W` suffix means **widening** — +the return value is widened from one common `R` to the union of every handler's +return type. -| Factory | Builder type | Return of the matcher | -| -------------------------------- | ------------------------------- | -------------------------------- | -| `getPrimitiveUnionMatcher()` | `PrimitiveUnionMatcher` | one common `R` | -| `getPrimitiveUnionMatcherW()` | `PrimitiveUnionMatcherW` | the union of the handler returns | -| `getTaggedUnionMatcher()` | `TaggedUnionMatcherFactory` | one common `R` | -| `getTaggedUnionMatcherW()` | `TaggedUnionMatcherWFactory` | the union of the handler returns | +| Factory | Return of the matcher | +| -------------------------------- | -------------------------------- | +| `getPrimitiveUnionMatcher()` | one common `R` | +| `getPrimitiveUnionMatcherW()` | the union of the handler returns | +| `getTaggedUnionMatcher()` | one common `R` | +| `getTaggedUnionMatcherW()` | the union of the handler returns | ### Primitive-union matchers diff --git a/development/library.md b/development/library.md index f1d3db1..909db0e 100644 --- a/development/library.md +++ b/development/library.md @@ -12,32 +12,30 @@ is implementation detail. #### Decision (2026-09) -`src/index.ts` exports the four factories and the builder types they return: -`PrimitiveUnionMatcher` / `PrimitiveUnionMatcherW`, `TaggedUnionMatcher` / -`TaggedUnionMatcherW`, and `TaggedUnionMatcherFactory` / -`TaggedUnionMatcherWFactory`. Every exported symbol carries TSDoc. The -universe-agnostic pieces in `matcher-shared.ts` stay internal. +`src/index.ts` exports the four factories and nothing else. Every exported +function carries TSDoc; the builder types and the `matcher-shared.ts` +vocabulary stay internal. #### Why -- The builder interfaces are already the inferred return types (they appear in - the emitted `.d.ts`), so exporting them only makes them nameable — a consumer - can annotate a factory result without restating its structure. -- The `Strict` / `Widening` suffixes were dropped for the public names because - the factory names already carry the `W` axis; the type and function names line - up (`getPrimitiveUnionMatcher` → `PrimitiveUnionMatcher`). +- The factories are the whole contract: a consumer calls one and never needs to + name the builder type it returns. +- The builder interfaces are already the inferred return types, so they travel + into the emitted `.d.ts` regardless. Exporting them would only make them + nameable while freezing the internal `Strict` / `Widening` overload split as + API. - TSDoc travels into the emitted declarations, so editor hovers and the published package document the API without a hand-written `.d.ts`. -- `matcher-shared.ts` stays internal: its types are plumbing (`Handlers`, - `Fallback`, `UniverseGate`, …) whose shape follows the implementation, and the - matchers are the stable contract. #### Rejected +- **Exporting the builder types** (`PrimitiveUnionMatcher`, …). Nameable, but it + grows the surface for no call-site benefit and pins the `Strict` / `Widening` + split. - **Exporting the `matcher-shared.ts` vocabulary** (`Matchable`, `UnaryFn`, `PatternKey`, `Member`, `PatternReturns`, …). They appear in the public signatures, but a consumer never needs to name them; exporting them would - freeze internals as API. + freeze plumbing as API. - **A hand-written `.d.ts` or a separate API document.** It would drift from the implementation; TSDoc is generated from the source. diff --git a/src/index.test.ts b/src/index.test.ts index ac45a15..861a941 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -8,18 +8,8 @@ import { getPrimitiveUnionMatcherW, getTaggedUnionMatcher, getTaggedUnionMatcherW, - type PrimitiveUnionMatcher, - type PrimitiveUnionMatcherW, - type TaggedUnionMatcher, - type TaggedUnionMatcherFactory, - type TaggedUnionMatcherW, - type TaggedUnionMatcherWFactory, } from "./index.ts"; -interface Shape { - readonly kind: "circle" | "square"; -} - // The published entry point is the barrel (`package.json` exports // `./dist/index.js`), so every factory must be reachable from here. Importing it // also loads the module, which is what lets c8's `--all` measure it — see @@ -35,42 +25,3 @@ test("index: the public entry point re-exports every matcher factory", () => { expectTypeOf(getTaggedUnionMatcherW).toBeFunction(); assert.equal(typeof getTaggedUnionMatcherW, "function"); }); - -// The builder types are part of the public contract: a consumer annotates a -// factory result with them, so the exported names must match the inferred -// return types. -test("index: the primitive-union factories return the exported builder types", () => { - // Act - const strict = getPrimitiveUnionMatcher<"a" | "b">(); - const widening = getPrimitiveUnionMatcherW<"a" | "b">(); - - // Assert - expectTypeOf(strict).toEqualTypeOf>(); - assert.equal(typeof strict, "function"); - expectTypeOf(widening).toEqualTypeOf>(); - assert.equal(typeof widening, "function"); -}); - -test("index: the tagged-union factories return the exported factory types", () => { - // Act - const strict = getTaggedUnionMatcher(); - const widening = getTaggedUnionMatcherW(); - - // Assert - expectTypeOf(strict).toEqualTypeOf>(); - assert.equal(typeof strict, "function"); - expectTypeOf(widening).toEqualTypeOf>(); - assert.equal(typeof widening, "function"); -}); - -test("index: the tagged-union key step returns the exported builder types", () => { - // Act - const strict = getTaggedUnionMatcher()("kind"); - const widening = getTaggedUnionMatcherW()("kind"); - - // Assert - expectTypeOf(strict).toEqualTypeOf>(); - assert.equal(typeof strict, "function"); - expectTypeOf(widening).toEqualTypeOf>(); - assert.equal(typeof widening, "function"); -}); diff --git a/src/index.ts b/src/index.ts index 62d22fd..be33df2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ /** * The public entry point of `tiny-pattern-ts`. * - * Exports the four matcher factories and the builder types they return. - * Everything else in `src/` is an implementation detail. + * Exports the four matcher factories and nothing else; the builder types they + * return and the rest of `src/` are implementation detail. * * @module */ @@ -10,17 +10,7 @@ export { getPrimitiveUnionMatcher, getPrimitiveUnionMatcherW, } from "./primitive-union.ts"; -export type { - PrimitiveUnionMatcher, - PrimitiveUnionMatcherW, -} from "./primitive-union.ts"; export { getTaggedUnionMatcher, getTaggedUnionMatcherW, } from "./tagged-union.ts"; -export type { - TaggedUnionMatcher, - TaggedUnionMatcherFactory, - TaggedUnionMatcherW, - TaggedUnionMatcherWFactory, -} from "./tagged-union.ts"; diff --git a/src/primitive-union.ts b/src/primitive-union.ts index 34a95e9..4ef4a5f 100644 --- a/src/primitive-union.ts +++ b/src/primitive-union.ts @@ -44,14 +44,7 @@ type MustBePartial = // Strict returns: one common `R`. Overload order is load-bearing: // #1 Handlers (first) -> the exhaustive form and the autocomplete popup // #2 Fallback (last) -> accepts a partial handler map plus a fallback -/** - * The builder returned by {@link getPrimitiveUnionMatcher}: call it with a - * handler map for every member of `T` to get a strict matcher, or with a partial - * map plus a fallback. - * - * @typeParam T - The finite universe of primitive members to match. - */ -export interface PrimitiveUnionMatcher { +interface PrimitiveUnionMatcherStrict { (handlers: Handlers & UniverseGate): UnaryFn; < R, @@ -66,14 +59,7 @@ export interface PrimitiveUnionMatcher { // Widened returns: the union of every handler's return type. `P` is inferred // from the whole handler map, whose closed constraint supplies the // contextual/autocomplete type. -/** - * The builder returned by {@link getPrimitiveUnionMatcherW}: like - * {@link PrimitiveUnionMatcher}, but the result's return type is the union of - * every handler's return type instead of one common `R`. - * - * @typeParam T - The finite universe of primitive members to match. - */ -export interface PrimitiveUnionMatcherW { +interface PrimitiveUnionMatcherWidening {

, P>>( handlers: P & UniverseGate, ): UnaryFn>; @@ -130,7 +116,7 @@ const dispatch = */ export const getPrimitiveUnionMatcher = < T extends Matchable, ->(): PrimitiveUnionMatcher => dispatch; +>(): PrimitiveUnionMatcherStrict => dispatch; /** * Create a matcher for a finite primitive universe whose return type is the @@ -151,4 +137,4 @@ export const getPrimitiveUnionMatcher = < */ export const getPrimitiveUnionMatcherW = < T extends Matchable, ->(): PrimitiveUnionMatcherW => dispatch; +>(): PrimitiveUnionMatcherWidening => dispatch; diff --git a/src/tagged-union.ts b/src/tagged-union.ts index 58199b1..97fd2f6 100644 --- a/src/tagged-union.ts +++ b/src/tagged-union.ts @@ -87,15 +87,7 @@ type MustBePartial = // Strict returns: one common `R`. Overload order is load-bearing: // #1 Handlers (first) -> the exhaustive form and the autocomplete popup // #2 Fallback (last) -> accepts a partial handler map plus a fallback -/** - * The builder returned by the key step of {@link getTaggedUnionMatcher}: call it - * with a handler map for every tag under `K` to get a strict matcher, or with a - * partial map plus a fallback. - * - * @typeParam T - The discriminated-union type to match. - * @typeParam K - The discriminant property of `T`. - */ -export interface TaggedUnionMatcher { +interface TaggedUnionMatcherStrict { (handlers: Handlers & UniverseGate>): UnaryFn; < R, @@ -112,15 +104,7 @@ export interface TaggedUnionMatcher { // Widened returns: the union of every handler's return type. `P` is inferred // from the whole handler map, whose closed constraint supplies the // contextual/autocomplete type. -/** - * The builder returned by the key step of {@link getTaggedUnionMatcherW}: like - * {@link TaggedUnionMatcher}, but the result's return type is the union of every - * handler's return type instead of one common `R`. - * - * @typeParam T - The discriminated-union type to match. - * @typeParam K - The discriminant property of `T`. - */ -export interface TaggedUnionMatcherW { +interface TaggedUnionMatcherWidening {

, P>>( handlers: P & UniverseGate>, ): UnaryFn>; @@ -137,29 +121,15 @@ export interface TaggedUnionMatcherW { // The key-taking step of the curried factory. Naming it lets the factory return // `dispatch` directly, the tacit twin of the primitive-union factory's bare // `=> dispatch`. -/** - * The function returned by {@link getTaggedUnionMatcher}: call it with the - * discriminant property's name to get the handler-map builder. - * - * @typeParam T - The discriminated-union type to match. - */ -export type TaggedUnionMatcherFactory = < - K extends Discriminated, ->( +type TaggedUnionMatcherFactory = >( k: K, -) => TaggedUnionMatcher; +) => TaggedUnionMatcherStrict; -/** - * The function returned by {@link getTaggedUnionMatcherW}: call it with the - * discriminant property's name to get the widening handler-map builder. - * - * @typeParam T - The discriminated-union type to match. - */ -export type TaggedUnionMatcherWFactory = < +type TaggedUnionMatcherWideningFactory = < K extends Discriminated, >( k: K, -) => TaggedUnionMatcherW; +) => TaggedUnionMatcherWidening; const dispatch = (k: PropertyKey) => @@ -227,4 +197,4 @@ export const getTaggedUnionMatcher = < */ export const getTaggedUnionMatcherW = < T extends object, ->(): TaggedUnionMatcherWFactory => dispatch; +>(): TaggedUnionMatcherWideningFactory => dispatch;