fix(v4): export SmoothToRecord, the type the record overload returns - #873
Conversation
`smoothTo()` has two overloads. The scalar one returns `SmoothTo`, the
record one returns `SmoothToRecord<K>`. The utils barrel named the first
two interfaces of `smoothTo.ts` and stopped there, so the third never
reached `dist` and a consumer could call the record overload but not
write down what it got back:
// before
import { smoothTo, type SmoothToRecord } from '@studiometa/js-toolkit/utils';
// ^ has no exported member 'SmoothToRecord'
That is not a cosmetic gap. The return value of a public overload is part
of the public signature: anything holding it in a class field, a function
parameter or a return position has to name it. The `@studiometa/ui` v2
port hit exactly this and copied the interface into its own source, which
is a duplicate that will drift from ours the first time either moves.
Nothing else in the package surface reacts to the line. A type-only
symbol gets no subpath — `enumerate()` filters `isType` out, because a
subpath exists to keep a runtime import from dragging in a barrel's graph
and a type import is erased before anything runs — so the stubs, the
`exports` map and `check-doc-links.js`, which walks those stubs, are all
unchanged. Types stay reachable through `.` and `./utils`, which is the
entry point this fixes.
The two specs that assert the surface now say so, since nothing else
would have caught it: `utils/index.spec.ts` compares runtime keys and is
blind to types by construction, and `exports.spec.ts` reads the type
through the published `./utils` condition rather than through a relative
path, which is the way the consumer sees it.
Every module under `src/utils/` was checked against the barrel; this was
the only symbol missing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011izFBQT4AsFcD4tVZz1f7R
A patch release carrying one fix: `SmoothToRecord` is exported from `@studiometa/js-toolkit/utils`, so the return type of the record overload of `smoothTo()` is nameable from the published package. `@studiometa/eslint-plugin-js-toolkit` moves with the framework, as it did at alpha.0. Nothing in it changed; the two packages publish from this branch to the same `next` dist-tag and are easier to reason about when their versions match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011izFBQT4AsFcD4tVZz1f7R
Export sizeBundled per export with peer dependencies left external, dynamic imports excluded and the output minified; sizes are gzipped. ✅ No export size changes. Unchanged (196)@studiometa/js-toolkit
|
Code ReviewRisk: Low — The change only exposes the missing The utils barrel now re-exports Review usage: 8,758 in / 209 out tokens — $0.0061 (openrouter/openai/gpt-5.6-luna, thinking: low) Reviewed by @weareikko/code-review v0.9.5 for commit 90ec4b1. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #873 +/- ##
=======================================
Coverage 94.55% 94.55%
=======================================
Files 38 38
Lines 1176 1176
Branches 498 498
=======================================
Hits 1112 1112
Misses 57 57
Partials 7 7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
mount benchmarksBase and head measured on this runner, alternating over 3 rounds each; every value is the median of the round medians. Running both sides on one machine is what removes cross-machine noise — a cached baseline from another runner would put it back. A move under 25%, or on a benchmark under 5 ms, is not reported as a change: it is inside the measured noise of a shared runner. No benchmark moved beyond the noise floor. Within noise (18)
|
The defect
src/utils/smoothTo.tsdeclares three interfaces. The utils barrel named two of them:SmoothToRecord<K>is the return type of the record overload ofsmoothTo():So it is part of the public signature, but it never reached
dist. A consumer could call the overload and not write down what it got back:Why it matters to a consumer
The return value of a public overload has to be nameable. Anything holding the record in a class field, a function parameter or a return position needs the type; inference only carries you as far as the first
const. The@studiometa/uiv2 port hit exactly this and copied the interface into its own source — a duplicate that drifts from ours the first time either moves. That port is where the omission was found.Scope
Every module under
src/utils/was audited against the barrel.SmoothToRecordis the only symbol a module exports and the barrel does not. This is a one-line fix, not a pattern.What the public-surface checks obliged beyond the one line
Nothing in the generated surface, and two spec assertions.
npm run subpaths:check— unchanged, and deliberately so.enumerate()inscripts/lib/subpath-exports.jsfiltersisTypesymbols out: a subpath exists to keep one runtime import from dragging in a whole barrel's graph, and a type import is erased before anything runs. So no stub, noexportsentry. Types stay reachable through.and./utils, which is exactly the entry point this PR fixes. Verified by running the check, not assumed.check-doc-links.js— unchanged for the same reason: it walkssrc/subpaths/**/*.ts, and a type-only symbol has no stub to walk. It still reports its 193 exports.docs/utils/motion.mdalready documents the overload and already namesSmoothToRecord<K>in its signature block, so there was no docs gap to close either.src/utils/index.spec.tscomparesObject.keys(barrel)against the runtime keys of every module it fronts. It is blind to types by construction. Its companion test, "forwards the types too, which the runtime check cannot see", is where the type belongs, and now names it.src/exports.spec.tsreads the type through the published./utilscondition rather than a relative path, which is how the consumer sees it:expectTypeOf(smoothTo({ x: 0, y: 0 })).toEqualTypeOf<SmoothToRecord<'x' | 'y'>>().Release
4.0.0-alpha.1across the root manifest,@studiometa/js-toolkitand@studiometa/eslint-plugin-js-toolkit, withpackage-lock.jsonrefreshed and a### Fixedsection inCHANGELOG.md.Verification
Run from the repository root, on this branch.
The single
no-useless-spreadwarning insrc/context.tspredates this branch and is untouched.lint:typespasses for both workspaces.And the emitted declaration now carries the type —
dist/utils/index.d.ts:🤖 Generated with Claude Code
https://claude.ai/code/session_011izFBQT4AsFcD4tVZz1f7R