Major Changes
-
#494
2ecc799- Rewrite re-reselect in native TypeScript.re-reselect moved from JavaScript with a hand-maintained
index.d.ts(~4500 lines of per-arity overloads) to.tssources. The build generates the type declarations, which use reselect v5's variadic-tuple inference instead of hand-written overloads.tsdown(rolldown) replaces Rollup + Babel.The runtime API is unchanged. re-reselect still exports the same two factories (
createCachedSelector,createStructuredCachedSelector) and six cache classes, as CJS and ESM bundles.Runtime behavior changes
resetRecomputations()now returnsundefinedinstead of0and is typed() => void(aligned with reselect v5). Userecomputations()to read the count.- A missing
keySelectornow throws when the selector is created, not on the first call.createCachedSelector(...)({ cacheObject })used to build a selector that threw aTypeErroronly when called. Now the factory throws[re-reselect] Missing "keySelector". Selectors usually live at module scope, so a selector already broken this way now fails at import time. - re-reselect passes an invalid
cacheKeythroughString()before writing the warning. Previously asymbolcacheKey made the warning itself throw aTypeError. - The cache lookup treats any falsy value returned by a custom
cacheObject.get()as a miss, rather than onlyundefined. A cache object that returnsnullfor a miss now works instead of crashing.
Type changes
The inferred type layer is stricter. Most of the changes below show up as compile errors in strict-mode projects; the one about unannotated input selectors weakens inference without an error.
-
Explicit type arguments no longer match. The per-arity generics (
createCachedSelector<S, R1, T>,createCachedSelector<S, P, R1, R2, T>,createStructuredCachedSelector<M, S, P>, …) are gone.createCachedSelectornow takes<InputSelectors, Result>andcreateStructuredCachedSelectortakes<InputSelectorsObject>, so old calls fail withTS2558. Drop the type arguments and annotatestateand params on the input selectors, or pass the new type arguments (createCachedSelector<[typeof selectA, typeof selectB], Result>(…)). -
The cached selector's call signature is now inferred from the input selectors, instead of
(state, props, ...args: any[]). reselect'sMergeParametersmerges the input selectors' parameters position by position. TypeScript now rejects excess arguments and missing arguments that an input selector declares. If two input selectors declare incompatible types at the same position, that argument becomesnever.getMatchingSelectorandremoveMatchingSelectortake the same arguments. -
An inline input selector without a
stateannotation, mixed with annotated ones, now infersstateasany, and thatanyreaches the combiner and the result. The previous hand-written.d.tsinferred it from the other input selectors. This is the same inference reselect v5'screateSelectordoes. Annotatestateon every inline input selector, or usecreateCachedSelector.withTypes<State>()with input selectors passed as an array. -
getMatchingSelector()is now typed as returningOutputSelector<…> | undefinedinstead ofOutputSelector<…>. It has always returnedundefinedat runtime when no selector is cached for the given arguments (not called yet, removed withremoveMatchingSelector/clearCache, or evicted by a size-limited cache). WithstrictNullChecks, check the result or use optional chaining:selector.getMatchingSelector(state, id)?.recomputations(). -
The
keySelectorparameter list is now inferred from the input selectors ((state, ...params)) instead of the permissive(state, ...args: any[]). AkeySelectorthat reads an extra argument not declared by any input selector no longer type-checks. This is the "cache dimension supplied viakeySelectoronly" pattern. Express the dimension as a parametric input selector ((state, id) => id) so it flows into both the call signature and thekeySelector. Runtime behavior is unchanged. -
keySelectorCreatornow receiveskeySelectoras an optional property (keySelector?: TypedKeySelector<InputSelectors>), because it is absent whenkeySelectorCreatoris used on its own. Existing creators that call it directly need a guard or a non-null assertion:// before ({ keySelector }) => (state, id) => keySelector(state, id); // after ({ keySelector }) => (state, id) => keySelector?.(state, id) ?? id;
-
The keySelector returned by
keySelectorCreatoris now typedTypedKeySelector<InputSelectors>instead of(state, ...args: any[]) => any, so it follows the same rule askeySelectorand can't read an argument no input selector declares. With input selectors taking(state, id), a creator returning(state, id, page) => …no longer type-checks. Movepageinto a parametric input selector. -
createStructuredCachedSelectorwith a very large selectors object (roughly 50+ keys) may fail to type-check withTS2589"Type instantiation is excessively deep". reselect v5's owncreateStructuredSelectorhas the same limit, because both derive a tuple from the object type. The old hand-written.d.tsavoided it with non-recursive mapped types. Runtime behavior is unchanged. Split the object into smaller structured selectors, or compose plaincreateCachedSelectorcalls.
Removed and changed type exports
Several exported types were removed or changed. Most mirrored reselect's own types; import those from
reselect, which is a peer dependency.Removed. Import from
reselectinstead:// before: import type { Selector, OutputSelector } from 're-reselect'; import type { OutputSelector, Selector } from 'reselect';
Selector,OutputSelector, andCreateSelectorInstance(use reselect'sCreateSelectorFunction).Removed. The parametric variants no longer exist. reselect v5 folds "parametric" into the params tuple of a single
Selector/OutputSelectortype, so these have no replacement to import. Express the props via the tuple instead:Removed Replacement ParametricSelector<S, P, R>Selector<S, R, [P]>OptionalParametricSelector<S, P, R>Selector<S, R, [P?]>OutputParametricSelector<…>let inference produce OutputSelector<[...inputs], R>OutputOptionalParametricSelector<…>let inference produce OutputSelector<[...inputs], R>OutputParametricCachedSelector<…>see OutputCachedSelectorbelowOutputOptionalParametricCachedSelector<…>see OutputCachedSelectorbelowParametricKeySelector<S, P>KeySelector<S>/TypedKeySelector<Inputs>ParametricKeySelectorCreator<…>KeySelectorCreator<Inputs, Result>Changed arity (aligned with reselect v5, which keys these on the input-selector array instead of
State):KeySelectorCreator<S, C, D>→KeySelectorCreator<InputSelectors, Result>
OutputCachedSelectornow types the selector, not the factory. It used to type the factory thatcreateCachedSelector(...)returns, which takes akeySelectoror options object. It now types the cached selector that factory returns, the same way reselect'sOutputSelectortypes whatcreateSelectorreturns. Its type arguments change to match reselect v5, fromOutputCachedSelector<S, R, C, D>toOutputCachedSelector<InputSelectors, Result>. Move the annotation from the factory to the selector:// before const makeSelectUser: OutputCachedSelector<State, User, Combiner, Deps> = createCachedSelector(selectUsers, selectId, (users, id) => users[id]); const selectUser = makeSelectUser((state, id) => id); // after const selectUser: OutputCachedSelector< [typeof selectUsers, typeof selectId], User > = createCachedSelector( selectUsers, selectId, (users, id) => users[id], )((state, id) => id);
Packaging changes
The package now ships a standard
exportsmap with separateimport(ESM) andrequire(CJS) conditions, each pointing at its own type declarations.- The
reselectpeer dependency is now^5.1.0. The generated types importSelectorsObject,SelectorResultsMapand the genericSelectorArray<State>/CreateSelectorFunction<..., StateType>, which reselect 5.0.x doesn't export. Runtime still works with 5.0.x. - Deep imports into
distare no longer resolvable. The package exposes only its root (re-reselect) andre-reselect/package.json. Import from the package root instead of paths likere-reselect/dist/.... - The UMD build and the
browserfield have been removed. re-reselect no longer ships adist/umd/bundle (reselect v5 dropped its own UMD build too). Any consumer loading re-reselect straight from a CDN via a<script>tag (unpkg, jsDelivr) or resolving the oldbrowserfield must switch to the ESM or CJS build. Use an ESM CDN such ashttps://esm.sh/re-reselect, or bundle re-reselect as part of an app build. - The ESM build is now
dist/es/index.mjs(wasdist/es/index.js), reached through theimportcondition, so native Node ESM loads it as a real ES module. - Default imports no longer work in native Node ESM. re-reselect has never had a default export, but
import reReselect from 're-reselect'used to load the CJS build through Node's interop and return the module object. Node now loads the real ES module, so the import fails withdoes not provide an export named 'default'. Use named imports orimport * as reReselect from 're-reselect'. - The
modulefield points at a separatedist/es/index.legacy-esm.js. Old bundlers such as webpack 4 readmodule, notexports. They treat a.mjsfile as strict ESM that can't re-export reselect's named bindings, so they get a.jslegacy-ESM entry instead, like reselect's ownreselect.legacy-esm.js. Node uses theimportcondition and never loads this file. - The bundles are no longer ES5. Babel compiled v5 to ES5. The CJS and
.mjsbuilds now use ES2020 syntax (class,?.,??, object spread) and the legacy-ESM build uses ES2019, matching reselect v5. Apps that target older engines must transpilenode_modules. - Type declarations moved. The
importcondition resolves todist/es/index.d.mtsand therequirecondition todist/cjs/index.d.ts, which the top-leveltypesfield also points at. Consumers resolving types through the package name are unaffected.
Minor Changes
-
#494
2ecc799- AddcreateCachedSelector.withTypes<State>(), a pre-typed selector creator that mirrors reselect'screateSelector.withTypes.Set the
statetype once and reuse it across everycreateCachedSelectorcall, so you no longer annotatestateon each input selector:const createAppCachedSelector = createCachedSelector.withTypes<RootState>();
Pass the input selectors as an array. As with reselect, the variadic form infers
stateasany.withTypesonly changes types. At runtime it returns the samecreateCachedSelector.