Understanding the NoInfer Type Parameter in Redux createStore
The NoInfer type parameter is an internal TypeScript utility that prevents the compiler from automatically inferring generic type arguments in Redux's createStore function, ensuring store extensions remain explicitly typed.
In the reduxjs/redux repository, the createStore function relies on sophisticated TypeScript generics to support store extensions—custom properties merged into the returned store object. The NoInfer utility type plays a critical role in maintaining type safety by blocking unwanted type inference.
What Is the NoInfer Type Parameter?
NoInfer is a conditional type utility defined internally in src/createStore.ts. It uses a tuple indexing trick to make a type "non-inferable" by TypeScript's inference engine:
// src/createStore.ts
type NoInfer<T> = [T][T extends any ? 0 : never]
This definition exploits the fact that TypeScript avoids inferring types that appear in contravariant positions or complex conditional expressions. When NoInfer<T> wraps a type parameter, the compiler treats T as explicitly provided rather than attempting to derive it from function arguments.
Why createStore Uses NoInfer
The createStore function signature includes four generic parameters: S (state), A (actions), Ext (extensions), and StateExt (state extensions). The Ext parameter represents an object type whose properties are merged into the returned store:
export function createStore<
S,
A extends Action,
Ext extends {} = {},
StateExt extends {} = {}
>(reducer: Reducer<S, A>, preloadedState?: S, enhancer?: StoreEnhancer<Ext, StateExt>):
Store<S, A, UnknownIfNonSpecific<StateExt>> & NoInfer<Ext>
Without NoInfer<Ext>, TypeScript would attempt to infer Ext from the enhancer argument or other parameters. Because Ext is only used to augment the return type—and not consumed as a function parameter in a way that constrains inference—the compiler might default it to {} or infer an incorrect type from context.
By wrapping the return type intersection with NoInfer<Ext>, Redux ensures that Ext must be explicitly specified by the caller or correctly propagated from the enhancer's type parameters, preserving the intended type augmentation.
Practical Examples
Basic Store Without Extensions
When creating a simple store without enhancers, Ext defaults to {} and NoInfer has no visible effect:
import { createStore } from 'redux'
function counter(state = 0, action: { type: string }) {
switch (action.type) {
case 'inc': return state + 1
default: return state
}
}
// Ext defaults to {}
const store = createStore(counter)
// Type: Store<number, { type: string }>
Store With Custom Extensions
When using a store enhancer that adds methods, NoInfer ensures the extension type propagates correctly:
interface LoggerExtension {
logState: () => void
}
// Explicitly provide the extension generic
const store = createStore<number, { type: string }, LoggerExtension>(counter)
// TypeScript recognizes the custom method
store.logState = () => console.log(store.getState())
store.logState() // → logs current state
The Inference Problem Without NoInfer
If createStore omitted NoInfer, TypeScript might incorrectly narrow Ext based on optional parameters:
// Hypothetical signature without NoInfer
function createStoreWithInfer<S, A extends Action, Ext extends {} = {}>(
reducer: Reducer<S, A>,
extensionFactory?: () => Ext
): Store<S, A> & Ext {
// implementation...
}
// TypeScript infers Ext = {} because extensionFactory is optional
const store = createStoreWithInfer(counter)
// Resulting type lacks extension properties even if provided later
Using NoInfer<Ext> blocks this unwanted inference, forcing the type system to respect the explicit generic parameter.
Key Implementation Files
The NoInfer utility and its application appear in these critical source files:
src/createStore.ts– Defines theNoInfer<T>utility type and applies it to the return type ofcreateStoreandlegacy_createStoresrc/types/store.ts– Contains theStoreinterface that gets augmented by theExtgenericsrc/types/actions.ts– Provides the baseActiontype used in the store's generic constraints
Summary
NoInfer<T>is an internal TypeScript utility in Redux that prevents automatic type inference for specific generic parameters.- It ensures that store extension types (
Ext) must be explicitly provided rather than inferred from function arguments. - The type uses a tuple indexing trick (
[T][T extends any ? 0 : never]) to block TypeScript's inference engine. - This mechanism maintains type safety when using store enhancers that add custom methods or properties to the store object.
Frequently Asked Questions
What does the NoInfer type parameter do in Redux?
The NoInfer type parameter prevents TypeScript from automatically inferring the Ext (extension) generic in createStore. Without it, the compiler might incorrectly infer the extension type from optional parameters or default it to {}, causing custom store methods to lose their type information. By wrapping the return type with NoInfer<Ext>, Redux ensures that extension types remain exactly as specified by the developer.
Is NoInfer part of Redux's public API?
No, NoInfer is an internal implementation detail and is not exported from the Redux package. Developers using Redux do not interact with NoInfer directly; it operates behind the scenes to maintain type correctness when createStore returns a store object augmented with extension properties. The utility is defined privately in src/createStore.ts and only appears in the return type signature.
Why can't TypeScript infer store extensions automatically?
TypeScript's inference engine prioritizes types derived from function parameters over return type expectations. Since the Ext generic only appears in the return type (as part of the intersection Store & NoInfer<Ext>) and not in a parameter position that constrains it, the compiler has no reliable source for inference. If NoInfer were absent, TypeScript would likely default Ext to {} or infer it from unrelated context, breaking the type augmentation pattern used by store enhancers.
How does the NoInfer utility type actually work?
NoInfer<T> uses a conditional type within a tuple index to block inference: [T][T extends any ? 0 : never]. This construction places T in a contravariant position within a conditional expression, which signals to TypeScript's inference engine that this type should not be inferred from usage. The result is that T must be explicitly provided at the call site, preserving the intended type structure for store extensions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →