Repository: epeli/immer-reducer
Branch: master
Commit: e1a2d471e0db
Files: 18
Total size: 49.2 KB
Directory structure:
gitextract_sosauq7j/
├── .gitignore
├── .prettierrc
├── .travis.yml
├── .vscode/
│ └── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __dtslint__/
│ ├── generic-reducers.dtslint.ts
│ └── immer-reducer.dtslint.ts
├── __tests__/
│ ├── immer-reducer.test.tsx
│ └── use-reducer-integration.test.tsx
├── jest.config.js
├── package.json
├── src/
│ └── immer-reducer.ts
├── tsconfig.build.json
├── tsconfig.dtslint.json
├── tsconfig.json
└── tslint.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/node_modules
/package-lock.json
/lib
================================================
FILE: .prettierrc
================================================
{
"bracketSpacing": false,
"trailingComma": "all",
"tabWidth": 4
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- 10
- 8
script: npm test
================================================
FILE: .vscode/settings.json
================================================
{
"typescript.tsdk": "node_modules/typescript/lib"
}
================================================
FILE: CHANGELOG.md
================================================
See <https://github.com/epeli/immer-reducer/releases>
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Esa-Matti Suuronen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# immer-reducer
Type-safe and terse reducers with Typescript for React Hooks and Redux using [Immer](https://immerjs.github.io/immer/)!
## 📦 Install
npm install immer-reducer
You can also install [eslint-plugin-immer-reducer](https://github.com/skoshy/eslint-plugin-immer-reducer) to help you avoid errors when writing your reducer.
## 💪 Motivation
Turn this 💩 💩 💩
```ts
interface SetFirstNameAction {
type: "SET_FIRST_NAME";
firstName: string;
}
interface SetLastNameAction {
type: "SET_LAST_NAME";
lastName: string;
}
type Action = SetFirstNameAction | SetLastNameAction;
function reducer(action: Action, state: State): State {
switch (action.type) {
case "SET_FIRST_NAME":
return {
...state,
user: {
...state.user,
firstName: action.firstName,
},
};
case "SET_LAST_NAME":
return {
...state,
user: {
...state.user,
lastName: action.lastName,
},
};
default:
return state;
}
}
```
✨✨ Into this! ✨✨
```ts
import {ImmerReducer} from "immer-reducer";
class MyImmerReducer extends ImmerReducer<State> {
setFirstName(firstName: string) {
this.draftState.user.firstName = firstName;
}
setLastName(lastName: string) {
this.draftState.user.lastName = lastName;
}
}
```
🔥🔥 **Without losing type-safety!** 🔥🔥
Oh, and you get the action creators for free! 🤗 🎂
## 📖 Usage
Generate Action Creators and the actual reducer function for Redux from the class with
```ts
import {createStore} from "redux";
import {createActionCreators, createReducerFunction} from "immer-reducer";
const initialState: State = {
user: {
firstName: "",
lastName: "",
},
};
const ActionCreators = createActionCreators(MyImmerReducer);
const reducerFunction = createReducerFunction(MyImmerReducer, initialState);
const store = createStore(reducerFunction);
```
Dispatch some actions
```ts
store.dispatch(ActionCreators.setFirstName("Charlie"));
store.dispatch(ActionCreators.setLastName("Brown"));
expect(store.getState().user.firstName).toEqual("Charlie");
expect(store.getState().user.lastName).toEqual("Brown");
```
## 🌟 Typed Action Creators!
The generated `ActionCreator` object respect the types used in the class
```ts
const action = ActionCreators.setFirstName("Charlie");
action.payload; // Has the type of string
ActionCreators.setFirstName(1); // Type error. Needs string.
ActionCreators.setWAT("Charlie"); // Type error. Unknown method
```
If the reducer class where to have a method which takes more than one argument
the payload would be array of the arguments
```ts
// In the Reducer class:
// setName(firstName: string, lastName: string) {}
const action = ActionCreators.setName("Charlie", "Brown");
action.payload; // will have value ["Charlie", "Brown"] and type [string, string]
```
The reducer function is also typed properly
```ts
const reducer = createReducerFunction(MyImmerReducer);
reducer(initialState, ActionCreators.setFirstName("Charlie")); // OK
reducer(initialState, {type: "WAT"}); // Type error
reducer({wat: "bad state"}, ActionCreators.setFirstName("Charlie")); // Type error
```
## ⚓ React Hooks
Because the `useReducer()` API in React Hooks is the same as with Redux
Reducers immer-reducer can be used with as is.
```tsx
const initialState = {message: ""};
class ReducerClass extends ImmerReducer<typeof initialState> {
setMessage(message: string) {
this.draftState.message = message;
}
}
const ActionCreators = createActionCreators(ReducerClass);
const reducerFunction = createReducerFunction(ReducerClass);
function Hello() {
const [state, dispatch] = React.useReducer(reducerFunction, initialState);
return (
<button
data-testid="button"
onClick={() => {
dispatch(ActionCreators.setMessage("Hello!"));
}}
>
{state.message}
</button>
);
}
```
The returned state and dispatch functions will be typed as you would expect.
## 🤔 How
Under the hood the class is deconstructed to following actions:
```js
{
type: "IMMER_REDUCER:MyImmerReducer#setFirstName",
payload: "Charlie",
}
{
type: "IMMER_REDUCER:MyImmerReducer#setLastName",
payload: "Brown",
}
{
type: "IMMER_REDUCER:MyImmerReducer#setName",
payload: ["Charlie", "Brown"],
args: true
}
```
So the class and method names become the Redux Action Types and the method
arguments become the action payloads. The reducer function will then match
these actions against the class and calls the appropriate methods with the
payload array spread to the arguments.
🚫 The format of the `action.type` string is internal to immer-reducer. If
you need to detect the actions use the provided type guards.
The generated reducer function executes the methods inside the `produce()`
function of Immer enabling the terse mutatable style updates.
## 🔄 Integrating with the Redux ecosystem
To integrate for example with the side effects libraries such as
[redux-observable](https://github.com/redux-observable/redux-observable/) and
[redux-saga](https://github.com/redux-saga/redux-saga), you can access the
generated action type using the `type` property of the action creator
function.
With redux-observable
```ts
// Get the action name to subscribe to
const setFirstNameActionTypeName = ActionCreators.setFirstName.type;
// Get the action type to have a type safe Epic
type SetFirstNameAction = ReturnType<typeof ActionCreators.setFirstName>;
const setFirstNameEpic: Epic<SetFirstNameAction> = action$ =>
action$
.ofType(setFirstNameActionTypeName)
.pipe(
// action.payload - recognized as string
map(action => action.payload.toUpperCase()),
...
);
```
With redux-saga
```ts
function* watchFirstNameChanges() {
yield takeEvery(ActionCreators.setFirstName.type, doStuff);
}
// or use the isActionFrom() to get all actions from a specific ImmerReducer
// action creators object
function* watchImmerActions() {
yield takeEvery(
(action: Action) => isActionFrom(action, MyImmerReducer),
handleImmerReducerAction,
);
}
function* handleImmerReducerAction(action: Actions<typeof MyImmerReducer>) {
// `action` is a union of action types
if (isAction(action, ActionCreators.setFirstName)) {
// with action of setFirstName
}
}
```
**Warning:** Due to how immer-reducers action generation works, adding default
parameters to the methods will NOT pass it to the action payload, which can
make your reducer impure and the values will not be available in middlewares.
```ts
class MyImmerReducer extends ImmerReducer<State> {
addItem (id: string = uuid()) {
this.draftState.ids.push([id])
}
}
immerActions.addItem() // generates empty payload { payload: [] }
```
As a workaround, create custom action creator wrappers that pass the default parameters instead.
```ts
class MyImmerReducer extends ImmerReducer<State> {
addItem (id) {
this.draftState.ids.push([id])
}
}
const actions = {
addItem: () => immerActions.addItem(id)
}
```
It is also recommended to install the ESLint plugin in the "Install" section
to alert you if you accidentally encounter this issue.
## 📚 Examples
Here's a more complete example with redux-saga and [redux-render-prop](https://github.com/epeli/redux-render-prop):
<https://github.com/epeli/typescript-redux-todoapp>
## 🃏 Tips and Tricks
You can replace the whole `draftState` with a new state if you'd like. This could be useful if you'd like to reset back to your initial state.
```ts
import {ImmerReducer} from "immer-reducer";
const initialState: State = {
user: {
firstName: "",
lastName: "",
},
};
class MyImmerReducer extends ImmerReducer<State> {
// omitting other reducer methods
reset() {
this.draftState = initialState;
}
}
```
## 📓 Helpers
The module exports following helpers
### `function isActionFrom(action, ReducerClass)`
Type guard for detecting whether the given action is generated by the given
reducer class. The detected type will be union of actions the class
generates.
Example
```ts
if (isActionFrom(someAction, ActionCreators)) {
// someAction now has type of
// {
// type: "setFirstName";
// payload: string;
// } | {
// type: "setLastName";
// payload: string;
// };
}
```
### `function isAction(action, actionCreator)`
Type guard for detecting specific actions generated by immer-reducer.
Example
```ts
if (isAction(someAction, ActionCreators.setFirstName)) {
someAction.payload; // Type checks to `string`
}
```
### `type Actions<ImmerReducerClass>`
Get union of the action types generated by the ImmerReducer class
Example
```ts
type MyActions = Actions<typeof MyImmerReducer>;
// Is the same as
type MyActions =
| {
type: "setFirstName";
payload: string;
}
| {
type: "setLastName";
payload: string;
};
```
### `function setPrefix(prefix: string)`
The default prefix in the generated action types is `IMMER_REDUCER`. Call
this customize it for your app.
Example
```ts
setPrefix("MY_APP");
```
### `function composeReducers<State>(...reducers)`
Utility that reduces actions by applying them through multiple reducers.
This helps in allowing you to split up your reducer logic to multiple `ImmerReducer`s
if they affect the same part of your state
Example
```ts
class MyNameReducer extends ImmerReducer<NamesState> {
setFirstName(firstName: string) {
this.draftState.firstName = firstName;
}
setLastName(lastName: string) {
this.draftState.lastName = lastName;
}
}
class MyAgeReducer extends ImmerReducer<AgeState> {
setAge(age: number) {
this.draftState.age = 8;
}
}
export const reducer = composeReducers(
createReducerFunction(MyNameReducer, initialState),
createReducerFunction(MyAgeReducer, initialState)
)
```
================================================
FILE: __dtslint__/generic-reducers.dtslint.ts
================================================
import {
ImmerReducer,
createReducerFunction,
createActionCreators,
ImmerReducerState,
} from "../src/immer-reducer";
interface AssignFail {
___: "it should not be possible to assign to me";
}
interface State {
foo: {
fooField1: string;
fooField2: number;
};
bar: {
barField1: number[];
barField2: RegExp;
};
}
const initialState: State = {
foo: {
fooField1: "a",
fooField2: 1,
},
bar: {
barField1: [1, 2],
barField2: /re/,
},
};
function createGenericReducer<T extends {[key: string]: unknown}>() {
return class GenericReducer extends ImmerReducer<T> {
set(part: Partial<T>) {
Object.assign(this.draftState, part);
}
};
}
const ReducerClassFoo = createGenericReducer<State["foo"]>();
const ReducerClassBar = createGenericReducer<State["bar"]>();
////////////////////
// Instance tests //
////////////////////
const ins = new ReducerClassFoo(initialState.foo, initialState.foo);
const state_test_1: State["foo"] = ins.state;
const state_test_2: State["foo"] = ins.draftState;
// cannot assign to wrong state (ie. was not any)
// $ExpectError
const state_test_3: AssignFail = ins.state;
// $ExpectError
const state_test_4: AssignFail = ins.draftState;
//////////////////////////
// Action Creator tests //
//////////////////////////
const ActionCreatorsFoo = createActionCreators(ReducerClassFoo);
const ActionCreatorsBar = createActionCreators(ReducerClassBar);
ActionCreatorsFoo.set({fooField1: "b"});
ActionCreatorsFoo.set({fooField2: 2});
ActionCreatorsBar.set({barField1: [8]});
ActionCreatorsBar.set({barField2: /ding/});
// Cannot set bad values
// $ExpectError
ActionCreatorsFoo.set({fooField1: 2});
// Cannot set unknown fields
// $ExpectError
ActionCreatorsFoo.set({bad: 2});
// Cannot set bar fields
// $ExpectError
ActionCreatorsFoo.set({barField1: [8]});
////////////////////////////
// Reducer function tests //
////////////////////////////
const reducerFoo = createReducerFunction(ReducerClassFoo, initialState.foo);
reducerFoo(initialState, ActionCreatorsFoo.set({fooField1: "c"}));
// no bad actions allowed
// $ExpectError
reducerFoo(initialState, {type: "BAD_ACTION"});
// XXX bug! :( State is any here. This should fail!
reducerFoo({bad: "state"}, ActionCreatorsFoo.set({fooField1: "c"}));
// For some reason ImmerReducerState cannot infer state
// from a generic class. Maybe this is a limitation in Typescript?
type InferredState = ImmerReducerState<typeof ReducerClassFoo>;
declare const inferredState: InferredState;
// XXX! Should fail too!
const anumber: AssignFail = inferredState;
================================================
FILE: __dtslint__/immer-reducer.dtslint.ts
================================================
import {Action, createStore, bindActionCreators} from "redux";
import {
ImmerReducer,
createActionCreators,
createReducerFunction,
isAction,
Actions,
isActionFrom,
} from "../src/immer-reducer";
import {Dispatch} from "react";
import React from "react";
interface AssertNotAny {
___: "it should not be possible to assign to me";
}
interface State {
readonly foo: string;
readonly bar: number;
}
class MyReducer extends ImmerReducer<State> {
setBoth(newFoo: string, newBar: number) {
this.setBar(newBar);
this.setFoo(newFoo);
}
setFoo(newFoo: string) {
this.draftState.foo = newFoo;
}
setBar(newBar: number) {
this.draftState.bar = newBar;
}
setFooStatic() {
this.draftState.foo = "static";
}
}
////////////////////
// Test action types
////////////////////
const ActionCreators = createActionCreators(MyReducer);
// Action creator return Action Object
const action: {
type: "setBar";
payload: number;
} = ActionCreators.setBar(3);
// the action creator does no return any
// $ExpectError
const is_not_any: AssertNotAny = ActionCreators.setBar(3);
// actions without payload
const staticAction = ActionCreators.setFooStatic();
const staticPayload: [] = staticAction.payload;
// Actions with multiple items in the payload
const bothAction = ActionCreators.setBoth("foo", 1);
const bothPayload: [string, number] = bothAction.payload;
// Only function properties are picked
// $ExpectError
ActionCreators.draftState;
// $ExpectError
ActionCreators.state;
// Do not allow bad argument types
// $ExpectError
ActionCreators.setBar("sdf");
// Do not allow bad method names
// $ExpectError
ActionCreators.setBad(3);
//////////////////////
// Test reducer types
//////////////////////
class BadReducer {
dong() {}
}
// Cannot create action creators from random classes
// $ExpectError
createActionCreators(BadReducer);
const reducer = createReducerFunction(MyReducer);
// can create with proper initial state
createReducerFunction(MyReducer, {foo: "", bar: 0});
// Bad state argument is not allowed
// $ExpectError
createReducerFunction(MyReducer, {bad: "state"});
const newState: State = reducer(
{foo: "sdf", bar: 2},
{
type: "setBar",
payload: 3,
},
);
// reducer does not return any
// $ExpectError
const no_any_state: AssertNotAny = reducer(
{foo: "f", bar: 2},
{
type: "setBar",
payload: 3,
},
);
// bad state for the reducer
reducer(
// $ExpectError
{foo: "sdf", bar: "should be number"},
{
type: "setBar",
payload: 3,
},
);
// Bad action object
// $ExpectError
reducer({foo: "sdf", bar: 2}, {});
// Bad payload type
reducer(
{foo: "sdf", bar: 2},
// $ExpectError
{
type: "setBar",
payload: "should be number here",
},
);
// Bad action type
reducer(
{foo: "sdf", bar: 2},
{
// $ExpectError
type: "bad",
payload: 3,
},
);
reducer({foo: "sdf", bar: 2}, ActionCreators.setBar(3));
class OtherReducer extends ImmerReducer<State> {
setDing(dong: string) {
this.draftState.foo = dong;
}
}
const OtherActionCreators = createActionCreators(OtherReducer);
// Mixed reducer and action creators from different ImmerReducer classes
// $ExpectError
reducer({foo: "sdf", bar: 2}, OtherActionCreators.setDing("sdf"));
// Action creator provides action type
const actionType: "setBar" = ActionCreators.setBar.type;
// $ExpectError
const actionType_not_any: AssertNotAny = ActionCreators.setBar.type;
//////////////////////
// Test isAction types
//////////////////////
declare const unknownAction: {type: string};
if (isAction(unknownAction, ActionCreators.setBar)) {
// $ExpectError
const actione_not_any: AssertNotAny = unknownAction;
const knownAction: {
type: "setBar";
payload: number;
} = unknownAction;
// $ExpectError
const nope: string = unknownAction.payload;
}
/////////////////////////////
// Test Actions<> type helper
/////////////////////////////
class Reducer1 extends ImmerReducer<State> {
setFoo(newFoo: string) {
this.draftState.foo = newFoo;
}
setBar(newBar: number) {
this.draftState.bar = newBar;
}
}
type MyActions = Actions<typeof Reducer1>;
declare const someActions: MyActions;
// $ExpectError
const someActionsNotAny: AssertNotAny = someActions;
const someActionsTest:
| {
type: "setFoo";
payload: string;
}
| {
type: "setBar";
payload: number;
} = someActions;
type MyReducerActions = Actions<typeof Reducer1>;
declare const myReducerActions: MyReducerActions;
// $ExpectError
const actions_not_any: AssertNotAny = myReducerActions;
const actions_manual:
| {
type: "setFoo";
payload: string;
}
| {
type: "setBar";
payload: number;
} = myReducerActions;
//////////////////////////
// Test isActionFrom types
//////////////////////////
declare const someAction: Action;
const ActionCreators1 = createActionCreators(Reducer1);
if (isActionFrom(someAction, Reducer1)) {
// $ExpectError
const notany: AssertNotAny = someAction;
const actions_manual:
| {
type: "setFoo";
payload: string;
}
| {
type: "setBar";
payload: number;
} = someAction;
}
test("Can work with bindActionCreators", () => {
const initialState = {foo: ""};
const store = createStore(s => initialState);
class Reducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {}
}
const ActionCreators = createActionCreators(Reducer);
const boundActionCreators = bindActionCreators(
ActionCreators,
store.dispatch,
);
});
test("can use with React.useReducer()", () => {
const initialState = {foo: ""};
class Reducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {}
}
const ActionCreators = createActionCreators(Reducer);
const reducerFuntion = createReducerFunction(Reducer);
function Component1() {
const [state, dispatch] = React.useReducer(
reducerFuntion,
initialState,
);
const callback = () => {
dispatch(ActionCreators.setFoo("test"));
// $ExpectError
dispatch("bad");
const foo: string = state.foo;
// $ExpectError
const bar: AssertNotAny = state.foo;
};
return null;
}
});
================================================
FILE: __tests__/immer-reducer.test.tsx
================================================
import {
ImmerReducer,
createReducerFunction,
createActionCreators,
composeReducers,
setPrefix,
_clearKnownClasses,
isAction,
isActionFrom,
} from "../src/immer-reducer";
import {createStore, combineReducers, Action} from "redux";
beforeEach(_clearKnownClasses);
afterEach(() => {
setPrefix("IMMER_REDUCER");
});
test("can detect inherited actions", () => {
class Parent extends ImmerReducer<any> {
setFoo(foo: string) {}
}
class Child extends Parent {
setFoo2(foo: string) {}
}
const actions = createActionCreators(Child);
expect(actions.setFoo).toBeTruthy();
expect(actions.setFoo2).toBeTruthy();
});
test("can create reducers", () => {
const initialState = {foo: "bar"};
class TestReducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
}
const reducer = createReducerFunction(TestReducer);
const store = createStore(reducer, initialState);
expect(store.getState()).toEqual({foo: "bar"});
});
test("the reducer can return the initial state", () => {
const initialState = {foo: "bar"};
class TestReducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
}
const reducer = createReducerFunction(TestReducer, initialState);
const store = createStore(reducer);
expect(store.getState()).toEqual({foo: "bar"});
});
test("can dispatch actions", () => {
const initialState = {foo: "bar"};
class TestReducer extends ImmerReducer<typeof initialState> {
noop() {}
}
const ActionCreators = createActionCreators(TestReducer);
const reducer = createReducerFunction(TestReducer, initialState);
const store = createStore(reducer);
store.dispatch(ActionCreators.noop());
expect(store.getState()).toEqual({foo: "bar"});
});
test("can update state", () => {
const initialState = {foo: "bar"};
class TestReducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
}
const ActionCreators = createActionCreators(TestReducer);
const reducer = createReducerFunction(TestReducer, initialState);
const store = createStore(reducer);
store.dispatch(ActionCreators.setFoo("next"));
expect(store.getState()).toEqual({foo: "next"});
});
test("can update state using multiple methods", () => {
const initialState = {foo: "bar", bar: 1};
class TestReducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
setBar(bar: number) {
this.draftState.bar = bar;
}
setBoth(foo: string, bar: number) {
this.setFoo(foo);
this.setBar(bar);
}
}
const ActionCreators = createActionCreators(TestReducer);
const reducer = createReducerFunction(TestReducer, initialState);
const store = createStore(reducer);
store.dispatch(ActionCreators.setBoth("next", 2));
expect(store.getState()).toEqual({foo: "next", bar: 2});
});
test("the actual action type name is prefixed", () => {
const initialState = {foo: "bar"};
class TestReducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
}
const ActionCreators = createActionCreators(TestReducer);
const reducer = createReducerFunction(TestReducer, initialState);
const reducerSpy: typeof reducer = jest.fn(reducer);
const store = createStore(reducerSpy);
store.dispatch(ActionCreators.setFoo("next"));
expect(reducerSpy).toHaveBeenLastCalledWith(
{foo: "bar"},
{
payload: "next",
type: "IMMER_REDUCER:TestReducer#setFoo",
},
);
});
test("can add helpers to the class", () => {
const initialState = {foo: 1, bar: 1};
class Helper {
state: typeof initialState;
constructor(state: typeof initialState) {
this.state = state;
}
getCombined() {
return this.state.foo + this.state.bar;
}
}
class TestReducer extends ImmerReducer<typeof initialState> {
helper = new Helper(this.state);
combineToBar() {
this.draftState.bar = this.helper.getCombined();
}
}
const ActionCreators = createActionCreators(TestReducer);
const reducer = createReducerFunction(TestReducer, initialState);
const store = createStore(reducer);
store.dispatch(ActionCreators.combineToBar());
expect(store.getState()).toEqual({foo: 1, bar: 2});
});
test("can use combineReducers", () => {
interface State1 {
foo: number;
}
interface State2 {
bar: string;
}
class TestReducer1 extends ImmerReducer<State1> {
setFoo(foo: number) {
this.draftState.foo = foo;
}
}
class TestReducer2 extends ImmerReducer<State2> {
setBar(bar: string) {
this.draftState.bar = bar;
}
}
const ActionCreators1 = createActionCreators(TestReducer1);
const ActionCreators2 = createActionCreators(TestReducer2);
const slice1 = createReducerFunction(TestReducer1, {foo: 0});
const slice2 = createReducerFunction(TestReducer2, {bar: ""});
const combined = combineReducers({slice1, slice2});
const store = createStore(combined);
store.dispatch(ActionCreators1.setFoo(1));
store.dispatch(ActionCreators2.setBar("barval"));
const state: {
slice1: State1;
slice2: State2;
} = store.getState();
expect(state).toEqual({slice1: {foo: 1}, slice2: {bar: "barval"}});
});
test("cannot collide reducers", () => {
const initialState = {foo: "bar"};
class TestReducer1 extends ImmerReducer<typeof initialState> {
setFoo() {
this.draftState.foo = "1";
}
}
class TestReducer2 extends ImmerReducer<typeof initialState> {
setFoo() {
this.draftState.foo = "2";
}
}
const reducer = composeReducers(
createReducerFunction(TestReducer1),
createReducerFunction(TestReducer2),
);
const store = createStore(reducer, initialState);
const ActionCreators1 = createActionCreators(TestReducer1);
const ActionCreators2 = createActionCreators(TestReducer2);
store.dispatch(ActionCreators1.setFoo());
expect(store.getState()).toEqual({foo: "1"});
store.dispatch(ActionCreators2.setFoo());
expect(store.getState()).toEqual({foo: "2"});
});
test("dynamically generated reducers do not collide", () => {
const initialState = {
foo: "",
};
function createGenericReducer<T extends {[key: string]: unknown}>(
value: string,
) {
return class GenericReducer extends ImmerReducer<T> {
set() {
Object.assign(this.draftState, {foo: value});
}
};
}
const ReducerClass1 = createGenericReducer<typeof initialState>("1");
const ReducerClass2 = createGenericReducer<typeof initialState>("2");
const reducer1 = createReducerFunction(ReducerClass1, initialState);
const reducer2 = createReducerFunction(ReducerClass2, initialState);
const reducer = composeReducers(reducer1, reducer2);
const ActionCreators1 = createActionCreators(ReducerClass1);
const ActionCreators2 = createActionCreators(ReducerClass2);
const store = createStore(reducer);
store.dispatch(ActionCreators1.set());
expect(store.getState().foo).toEqual("1");
store.dispatch(ActionCreators2.set());
expect(store.getState().foo).toEqual("2");
});
test("can create dynamic reducers after creating actions", () => {
const initialState = {
foo: "",
};
function createGenericReducer<T extends {[key: string]: unknown}>(
value: string,
) {
return class GenericReducer extends ImmerReducer<T> {
set() {
Object.assign(this.draftState, {foo: value});
}
};
}
const ReducerClass1 = createGenericReducer<typeof initialState>("1");
const ReducerClass2 = createGenericReducer<typeof initialState>("2");
const ActionCreators1 = createActionCreators(ReducerClass1);
const ActionCreators2 = createActionCreators(ReducerClass2);
const reducer1 = createReducerFunction(ReducerClass1, initialState);
const reducer2 = createReducerFunction(ReducerClass2, initialState);
const reducer = composeReducers(reducer1, reducer2);
const store = createStore(reducer);
store.dispatch(ActionCreators1.set());
expect(store.getState().foo).toEqual("1");
store.dispatch(ActionCreators2.set());
expect(store.getState().foo).toEqual("2");
});
test("throw error when using duplicate customNames", () => {
class Reducer1 extends ImmerReducer<{foo: string}> {
static customName = "dup";
set() {
this.draftState.foo = "foo";
}
}
class Reducer2 extends ImmerReducer<{foo: string}> {
static customName = "dup";
set() {
this.draftState.foo = "foo";
}
}
createReducerFunction(Reducer1);
expect(() => {
createReducerFunction(Reducer2);
}).toThrow();
});
test("action creators expose the actual action type name", () => {
const initialState = {foo: "bar"};
class TestReducer extends ImmerReducer<typeof initialState> {
setBar(foo: string) {
this.draftState.foo = foo;
}
}
const ActionCreators = createActionCreators(TestReducer);
expect(ActionCreators.setBar.type).toEqual(
"IMMER_REDUCER:TestReducer#setBar",
);
});
test("can customize prefix of action type name what is returned by action creator.", () => {
const initialState = {foo: "bar"};
class TestReducer extends ImmerReducer<typeof initialState> {
setBar(foo: string) {
this.draftState.foo = foo;
}
}
setPrefix("AWESOME_LIBRARY");
const ActionCreators = createActionCreators(TestReducer);
expect(ActionCreators.setBar.type).toEqual(
"AWESOME_LIBRARY:TestReducer#setBar",
);
const reducer = createReducerFunction(TestReducer);
const store = createStore(reducer, initialState);
store.dispatch(ActionCreators.setBar("ding"));
expect(store.getState()).toEqual({foo: "ding"});
});
test("isActionFrom can detect actions", () => {
class TestReducer extends ImmerReducer<{foo: string}> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
}
const ActionCreators = createActionCreators(TestReducer);
const action1: Action = ActionCreators.setFoo("foo");
const action2: Action = {
type: "other",
};
expect(isActionFrom(action1, TestReducer)).toBe(true);
expect(isActionFrom(action2, TestReducer)).toBe(false);
});
test("isAction can detect actions", () => {
class TestReducer extends ImmerReducer<{foo: string}> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
}
const ActionCreators = createActionCreators(TestReducer);
const action1: Action = ActionCreators.setFoo("foo");
const action2: Action = {
type: "other",
};
expect(isAction(action1, ActionCreators.setFoo)).toBe(true);
expect(isAction(action2, ActionCreators.setFoo)).toBe(false);
});
test("single argument is the payload value", () => {
class TestReducer extends ImmerReducer<{}> {
singleArg(arg: string) {}
}
const action = createActionCreators(TestReducer).singleArg("foo");
expect(action.payload).toEqual("foo");
});
test("multiple arguments are as an array in the payload", () => {
class TestReducer extends ImmerReducer<{}> {
multiple(arg1: string, arg2: number) {}
}
const action = createActionCreators(TestReducer).multiple("foo", 2);
expect(action.payload).toEqual(["foo", 2]);
});
test("single argument can be an array", () => {
class TestReducer extends ImmerReducer<{}> {
singleArg(arg: string[]) {}
}
const action = createActionCreators(TestReducer).singleArg(["foo"]);
expect(action.payload).toEqual(["foo"]);
});
test("single array argument is dispatched correctly", () => {
expect.assertions(1);
class TestReducer extends ImmerReducer<{}> {
arrayArg(arr: string[]) {
expect(arr).toEqual(["foo", "bar"]);
}
}
const store = createStore(createReducerFunction(TestReducer, {}));
store.dispatch(createActionCreators(TestReducer).arrayArg(["foo", "bar"]));
});
test("puts only defined arguments to the action object", () => {
class TestReducer extends ImmerReducer<{}> {
doIt() {}
}
// Simulate click handler type
let onClick = (arg: string): any => {};
// "Pass action the event handler"
onClick = createActionCreators(TestReducer).doIt;
const action = onClick("nope");
expect(action.payload).toEqual([]);
});
test("puts only defined arguments to the action object", () => {
class TestReducer extends ImmerReducer<{}> {
doIt(oneArg: string) {}
}
// Simulate click handler type
let onClick = (first: string, second: string): any => {};
// "Pass action the event handler"
onClick = createActionCreators(TestReducer).doIt;
const action = onClick("yes", "nope");
expect(action.payload).toEqual("yes");
});
test("can replace the draft state with completely new state", () => {
const initialState = {foo: "bar", ding: "ding"};
class TestReducer extends ImmerReducer<typeof initialState> {
resetState() {
this.draftState = {
foo: "new",
ding: "new",
};
}
}
const ActionCreators = createActionCreators(TestReducer);
const reducer = createReducerFunction(TestReducer);
const store = createStore(reducer, initialState);
store.dispatch(ActionCreators.resetState());
expect(store.getState()).toEqual({
foo: "new",
ding: "new",
});
});
================================================
FILE: __tests__/use-reducer-integration.test.tsx
================================================
import React from "react";
import {render, fireEvent, cleanup} from "@testing-library/react";
import {
ImmerReducer,
createActionCreators,
createReducerFunction,
} from "../src/immer-reducer";
afterEach(cleanup);
test("can use with React.useReducer()", () => {
const initialState = {foo: ""};
class Reducer extends ImmerReducer<typeof initialState> {
setFoo(foo: string) {
this.draftState.foo = foo;
}
}
const ActionCreators = createActionCreators(Reducer);
const reducerFuntion = createReducerFunction(Reducer);
function Foo() {
const [state, dispatch] = React.useReducer(
reducerFuntion,
initialState,
);
return (
<button
data-testid="button"
onClick={() => {
dispatch(ActionCreators.setFoo("clicked"));
}}
>
{state.foo}
</button>
);
}
const rtl = render(<Foo />);
const button = rtl.getByTestId("button");
fireEvent.click(button);
expect(button.innerHTML).toBe("clicked");
});
================================================
FILE: jest.config.js
================================================
module.exports = {
moduleFileExtensions: ["ts", "tsx", "js"],
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
},
globals: {
"ts-jest": {
tsconfig: "tsconfig.json",
},
},
testMatch: ["**/?(*.)+(spec|test).ts?(x)"],
};
================================================
FILE: package.json
================================================
{
"name": "immer-reducer",
"version": "0.7.13",
"description": "",
"main": "lib/immer-reducer.js",
"types": "lib/immer-reducer.d.ts",
"repository": {
"url": "https://github.com/epeli/immer-reducer"
},
"scripts": {
"test": "npm run dtslint && jest",
"build": "tsc --project tsconfig.build.json && rm -rf lib && mv build/src lib && rm -rf build",
"clean": "rm -rf lib build",
"dtslint": "tslint --project tsconfig.dtslint.json",
"prepublishOnly": "npm run test && npm run build"
},
"keywords": [
"typescript",
"immer"
],
"author": "",
"license": "MIT",
"files": [
"lib"
],
"devDependencies": {
"@testing-library/react": "^8.0.4",
"@types/jest": "^24.0.15",
"@types/react": "^16.8.22",
"@types/react-dom": "^16.8.4",
"@types/redux": "^3.6.0",
"dtslint": "^4.0.7",
"jest": "^26.6.3",
"prettier": "^1.18.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"redux": "^4.0.1",
"ts-jest": "^26.5.1",
"typescript": "^3.9.9"
},
"dependencies": {
"immer": "^1.4.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^8.0.0 || ^9.0.0"
}
}
================================================
FILE: src/immer-reducer.ts
================================================
import produce, {Draft} from "immer";
let actionTypePrefix = "IMMER_REDUCER";
/** get function arguments as tuple type */
type ArgumentsType<T> = T extends (...args: infer V) => any ? V : never;
/**
* Get the first value of tuple when the tuple length is 1 otherwise return the
* whole tuple
*/
type FirstOrAll<T> = T extends [infer V] ? V : T;
/** Get union of function property names */
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type MethodObject = {[key: string]: () => any};
/** Pick only methods from object */
type Methods<T> = Pick<T, FunctionPropertyNames<T>>;
/** flatten functions in an object to their return values */
type FlattenToReturnTypes<T extends MethodObject> = {
[K in keyof T]: ReturnType<T[K]>;
};
/** get union of object value types */
type ObjectValueTypes<T> = T[keyof T];
/** get union of object method return types */
type ReturnTypeUnion<T extends MethodObject> = ObjectValueTypes<
FlattenToReturnTypes<T>
>;
/**
* Get union of actions types from a ImmerReducer class
*/
export type Actions<T extends ImmerReducerClass> = ReturnTypeUnion<
ActionCreators<T>
>;
/** type constraint for the ImmerReducer class */
export interface ImmerReducerClass {
customName?: string;
new (...args: any[]): ImmerReducer<any>;
}
/** get state type from a ImmerReducer subclass */
export type ImmerReducerState<T> = T extends {
prototype: {
state: infer V;
};
}
? V
: never;
/** generate reducer function type from the ImmerReducer class */
export interface ImmerReducerFunction<T extends ImmerReducerClass> {
(
state: ImmerReducerState<T> | undefined,
action: ReturnTypeUnion<ActionCreators<T>>,
): ImmerReducerState<T>;
}
/** ActionCreator function interface with actual action type name */
interface ImmerActionCreator<ActionTypeType, Payload extends any[]> {
readonly type: ActionTypeType;
(...args: Payload): {
type: ActionTypeType;
payload: FirstOrAll<Payload>;
};
}
/** generate ActionCreators types from the ImmerReducer class */
export type ActionCreators<ClassActions extends ImmerReducerClass> = {
[K in keyof Methods<InstanceType<ClassActions>>]: ImmerActionCreator<
K,
ArgumentsType<InstanceType<ClassActions>[K]>
>;
};
/**
* Internal type for the action
*/
type ImmerAction =
| {
type: string;
payload: unknown;
args?: false;
}
| {
type: string;
payload: unknown[];
args: true;
};
/**
* Type guard for detecting actions created by immer reducer
*
* @param action any redux action
* @param immerActionCreator method from a ImmerReducer class
*/
export function isAction<A extends ImmerActionCreator<any, any>>(
action: {type: any},
immerActionCreator: A,
): action is ReturnType<A> {
return action.type === immerActionCreator.type;
}
function isActionFromClass<T extends ImmerReducerClass>(
action: {type: any},
immerReducerClass: T,
): action is Actions<T> {
if (typeof action.type !== "string") {
return false;
}
if (!action.type.startsWith(actionTypePrefix + ":")) {
return false;
}
const [className, methodName] = removePrefix(action.type).split("#");
if (className !== getReducerName(immerReducerClass)) {
return false;
}
if (typeof immerReducerClass.prototype[methodName] !== "function") {
return false;
}
return true;
}
export function isActionFrom<T extends ImmerReducerClass>(
action: {type: any},
immerReducerClass: T,
): action is Actions<T> {
return isActionFromClass(action, immerReducerClass);
}
interface Reducer<State> {
(state: State | undefined, action: any): State;
}
/**
* Combine multiple reducers into a single one
*
* @param reducers two or more reducer
*/
export function composeReducers<State>(
...reducers: Reducer<State | undefined>[]
): Reducer<State> {
return (state: any, action: any) => {
return (
reducers.reduce((state, subReducer) => {
if (typeof subReducer === "function") {
return subReducer(state, action);
}
return state;
}, state) || state
);
};
}
/** The actual ImmerReducer class */
export class ImmerReducer<T> {
static customName?: string;
readonly state: T;
draftState: Draft<T>; // Make read only states mutable using Draft
constructor(draftState: Draft<T>, state: T) {
this.state = state;
this.draftState = draftState;
}
}
function removePrefix(actionType: string) {
return actionType
.split(":")
.slice(1)
.join(":");
}
let KNOWN_REDUCER_CLASSES: typeof ImmerReducer[] = [];
const DUPLICATE_INCREMENTS: {[name: string]: number | undefined} = {};
/**
* Set customName for classes automatically if there is multiple reducers
* classes defined with the same name. This can occur accidentaly when using
* name mangling with minifiers.
*
* @param immerReducerClass
*/
function setCustomNameForDuplicates(immerReducerClass: typeof ImmerReducer) {
const hasSetCustomName = KNOWN_REDUCER_CLASSES.find(klass =>
Boolean(klass === immerReducerClass),
);
if (hasSetCustomName) {
return;
}
const duplicateCustomName =
immerReducerClass.customName &&
KNOWN_REDUCER_CLASSES.find(klass =>
Boolean(
klass.customName &&
klass.customName === immerReducerClass.customName,
),
);
if (duplicateCustomName) {
throw new Error(
`There is already customName ${immerReducerClass.customName} defined for ${duplicateCustomName.name}`,
);
}
const duplicate = KNOWN_REDUCER_CLASSES.find(
klass => klass.name === immerReducerClass.name,
);
if (duplicate && !duplicate.customName) {
let number = DUPLICATE_INCREMENTS[immerReducerClass.name];
if (number) {
number++;
} else {
number = 1;
}
DUPLICATE_INCREMENTS[immerReducerClass.name] = number;
immerReducerClass.customName = immerReducerClass.name + "_" + number;
}
KNOWN_REDUCER_CLASSES.push(immerReducerClass);
}
/**
* Convert function arguments to ImmerAction object
*/
function createImmerAction(type: string, args: unknown[]): ImmerAction {
if (args.length === 1) {
return {type, payload: args[0]};
}
return {
type,
payload: args,
args: true,
};
}
/**
* Get function arguments from the ImmerAction object
*/
function getArgsFromImmerAction(action: ImmerAction): unknown[] {
if (action.args) {
return action.payload;
}
return [action.payload];
}
function getAllPropertyNames(obj: object) {
const proto = Object.getPrototypeOf(obj);
const inherited: string[] = proto ? getAllPropertyNames(proto) : [];
return Object.getOwnPropertyNames(obj)
.concat(inherited)
.filter(
(propertyName, index, uniqueList) =>
uniqueList.indexOf(propertyName) === index,
);
}
export function createActionCreators<T extends ImmerReducerClass>(
immerReducerClass: T,
): ActionCreators<T> {
setCustomNameForDuplicates(immerReducerClass);
const actionCreators: {[key: string]: Function} = {};
const immerReducerProperties = getAllPropertyNames(ImmerReducer.prototype);
getAllPropertyNames(immerReducerClass.prototype).forEach(key => {
if (immerReducerProperties.includes(key)) {
return;
}
const method = immerReducerClass.prototype[key];
if (typeof method !== "function") {
return;
}
const type = `${actionTypePrefix}:${getReducerName(
immerReducerClass,
)}#${key}`;
const actionCreator = (...args: any[]) => {
// Make sure only the arguments are passed to the action object that
// are defined in the method
return createImmerAction(type, args.slice(0, method.length));
};
actionCreator.type = type;
actionCreators[key] = actionCreator;
});
return actionCreators as any; // typed in the function signature
}
function getReducerName(klass: {name: string; customName?: string}) {
const name = klass.customName || klass.name;
if (!name) {
throw new Error(
`immer-reducer failed to get reducer name for a class. Try adding 'static customName = "name"'`,
);
}
return name;
}
export function createReducerFunction<T extends ImmerReducerClass>(
immerReducerClass: T,
initialState?: ImmerReducerState<T>,
): ImmerReducerFunction<T> {
setCustomNameForDuplicates(immerReducerClass);
return function immerReducerFunction(state, action) {
if (state === undefined) {
state = initialState;
}
if (!isActionFromClass(action, immerReducerClass)) {
return state;
}
if (!state) {
throw new Error(
"ImmerReducer does not support undefined state. Pass initial state to createReducerFunction() or createStore()",
);
}
const [_, methodName] = removePrefix(action.type as string).split("#");
return produce(state, draftState => {
const reducers: any = new immerReducerClass(draftState, state);
reducers[methodName](...getArgsFromImmerAction(action as any));
// The reducer replaced the instance with completely new state so
// make that to be the next state
if (reducers.draftState !== draftState) {
return reducers.draftState;
}
return draftState;
// Workaround typing changes in Immer 9.x. This does not actually
// affect the exposed types by immer-reducer itself.
// Also using immer internally with anys like this allow us to
// support multiple versions of immer.
}) as any;
};
}
export function setPrefix(prefix: string): void {
actionTypePrefix = prefix;
}
/**
* INTERNAL! This is only for tests!
*/
export function _clearKnownClasses() {
KNOWN_REDUCER_CLASSES = [];
}
/**
* https://webpack.js.org/api/hot-module-replacement/#module-api
*/
interface WebpackModule {
hot?: {
status(): string;
addStatusHandler?: (handler: (status: string) => void) => void;
};
}
/**
* Webpack Module global if using Wepback
*/
declare const module: WebpackModule | undefined;
if (typeof module !== "undefined") {
// Clear classes on Webpack Hot Module replacement as it will mess up the
// duplicate checks appear
module.hot?.addStatusHandler?.(status => {
if (status === "prepare") {
_clearKnownClasses();
}
});
}
================================================
FILE: tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["__dtslint__"],
"compilerOptions": {
"sourceMap": true,
"noEmit": false,
"outDir": "./build",
"declaration": true,
"declarationDir": "./build"
}
}
================================================
FILE: tsconfig.dtslint.json
================================================
{
"extends": "./tsconfig.json",
"exclude": []
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noEmit": true,
"jsx": "react",
"lib": ["esnext", "dom"],
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"strict": true,
"esModuleInterop": true
}
}
================================================
FILE: tslint.json
================================================
{
"rulesDirectory": "./node_modules/dtslint/bin/rules",
"rules": {
"expect": true
}
}
gitextract_sosauq7j/ ├── .gitignore ├── .prettierrc ├── .travis.yml ├── .vscode/ │ └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __dtslint__/ │ ├── generic-reducers.dtslint.ts │ └── immer-reducer.dtslint.ts ├── __tests__/ │ ├── immer-reducer.test.tsx │ └── use-reducer-integration.test.tsx ├── jest.config.js ├── package.json ├── src/ │ └── immer-reducer.ts ├── tsconfig.build.json ├── tsconfig.dtslint.json ├── tsconfig.json └── tslint.json
SYMBOL INDEX (124 symbols across 5 files)
FILE: __dtslint__/generic-reducers.dtslint.ts
type AssignFail (line 8) | interface AssignFail {
type State (line 12) | interface State {
function createGenericReducer (line 35) | function createGenericReducer<T extends {[key: string]: unknown}>() {
type InferredState (line 103) | type InferredState = ImmerReducerState<typeof ReducerClassFoo>;
FILE: __dtslint__/immer-reducer.dtslint.ts
type AssertNotAny (line 14) | interface AssertNotAny {
type State (line 18) | interface State {
class MyReducer (line 23) | class MyReducer extends ImmerReducer<State> {
method setBoth (line 24) | setBoth(newFoo: string, newBar: number) {
method setFoo (line 29) | setFoo(newFoo: string) {
method setBar (line 33) | setBar(newBar: number) {
method setFooStatic (line 37) | setFooStatic() {
class BadReducer (line 85) | class BadReducer {
method dong (line 86) | dong() {}
class OtherReducer (line 156) | class OtherReducer extends ImmerReducer<State> {
method setDing (line 157) | setDing(dong: string) {
class Reducer1 (line 197) | class Reducer1 extends ImmerReducer<State> {
method setFoo (line 198) | setFoo(newFoo: string) {
method setBar (line 202) | setBar(newBar: number) {
type MyActions (line 207) | type MyActions = Actions<typeof Reducer1>;
type MyReducerActions (line 224) | type MyReducerActions = Actions<typeof Reducer1>;
class Reducer (line 267) | class Reducer extends ImmerReducer<typeof initialState> {
method setFoo (line 268) | setFoo(foo: string) {}
method setFoo (line 283) | setFoo(foo: string) {}
class Reducer (line 282) | class Reducer extends ImmerReducer<typeof initialState> {
method setFoo (line 268) | setFoo(foo: string) {}
method setFoo (line 283) | setFoo(foo: string) {}
function Component1 (line 289) | function Component1() {
FILE: __tests__/immer-reducer.test.tsx
class Parent (line 21) | class Parent extends ImmerReducer<any> {
method setFoo (line 22) | setFoo(foo: string) {}
class Child (line 25) | class Child extends Parent {
method setFoo2 (line 26) | setFoo2(foo: string) {}
class TestReducer (line 37) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 52) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 67) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 83) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 101) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 128) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class Helper (line 155) | class Helper {
method constructor (line 158) | constructor(state: typeof initialState) {
method getCombined (line 162) | getCombined() {
class TestReducer (line 167) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
type State1 (line 185) | interface State1 {
type State2 (line 189) | interface State2 {
class TestReducer1 (line 193) | class TestReducer1 extends ImmerReducer<State1> {
method setFoo (line 194) | setFoo(foo: number) {
method setFoo (line 230) | setFoo() {
class TestReducer2 (line 199) | class TestReducer2 extends ImmerReducer<State2> {
method setBar (line 200) | setBar(bar: string) {
method setFoo (line 236) | setFoo() {
class TestReducer1 (line 229) | class TestReducer1 extends ImmerReducer<typeof initialState> {
method setFoo (line 194) | setFoo(foo: number) {
method setFoo (line 230) | setFoo() {
class TestReducer2 (line 235) | class TestReducer2 extends ImmerReducer<typeof initialState> {
method setBar (line 200) | setBar(bar: string) {
method setFoo (line 236) | setFoo() {
function createGenericReducer (line 263) | function createGenericReducer<T extends {[key: string]: unknown}>(
function createGenericReducer (line 297) | function createGenericReducer<T extends {[key: string]: unknown}>(
class Reducer1 (line 327) | class Reducer1 extends ImmerReducer<{foo: string}> {
method set (line 329) | set() {
class Reducer2 (line 334) | class Reducer2 extends ImmerReducer<{foo: string}> {
method set (line 336) | set() {
class TestReducer (line 351) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 367) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 389) | class TestReducer extends ImmerReducer<{foo: string}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 407) | class TestReducer extends ImmerReducer<{foo: string}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 425) | class TestReducer extends ImmerReducer<{}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 433) | class TestReducer extends ImmerReducer<{}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 441) | class TestReducer extends ImmerReducer<{}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 451) | class TestReducer extends ImmerReducer<{}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 462) | class TestReducer extends ImmerReducer<{}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 478) | class TestReducer extends ImmerReducer<{}> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
class TestReducer (line 496) | class TestReducer extends ImmerReducer<typeof initialState> {
method setFoo (line 38) | setFoo(foo: string) {
method setFoo (line 53) | setFoo(foo: string) {
method noop (line 68) | noop() {}
method setFoo (line 84) | setFoo(foo: string) {
method setFoo (line 102) | setFoo(foo: string) {
method setBar (line 106) | setBar(bar: number) {
method setBoth (line 110) | setBoth(foo: string, bar: number) {
method setFoo (line 129) | setFoo(foo: string) {
method combineToBar (line 170) | combineToBar() {
method setBar (line 352) | setBar(foo: string) {
method setBar (line 368) | setBar(foo: string) {
method setFoo (line 390) | setFoo(foo: string) {
method setFoo (line 408) | setFoo(foo: string) {
method singleArg (line 426) | singleArg(arg: string) {}
method multiple (line 434) | multiple(arg1: string, arg2: number) {}
method singleArg (line 442) | singleArg(arg: string[]) {}
method arrayArg (line 452) | arrayArg(arr: string[]) {
method doIt (line 463) | doIt() {}
method doIt (line 479) | doIt(oneArg: string) {}
method resetState (line 497) | resetState() {
FILE: __tests__/use-reducer-integration.test.tsx
class Reducer (line 14) | class Reducer extends ImmerReducer<typeof initialState> {
method setFoo (line 15) | setFoo(foo: string) {
function Foo (line 23) | function Foo() {
FILE: src/immer-reducer.ts
type ArgumentsType (line 6) | type ArgumentsType<T> = T extends (...args: infer V) => any ? V : never;
type FirstOrAll (line 12) | type FirstOrAll<T> = T extends [infer V] ? V : T;
type FunctionPropertyNames (line 15) | type FunctionPropertyNames<T> = {
type MethodObject (line 19) | type MethodObject = {[key: string]: () => any};
type Methods (line 22) | type Methods<T> = Pick<T, FunctionPropertyNames<T>>;
type FlattenToReturnTypes (line 25) | type FlattenToReturnTypes<T extends MethodObject> = {
type ObjectValueTypes (line 30) | type ObjectValueTypes<T> = T[keyof T];
type ReturnTypeUnion (line 33) | type ReturnTypeUnion<T extends MethodObject> = ObjectValueTypes<
type Actions (line 40) | type Actions<T extends ImmerReducerClass> = ReturnTypeUnion<
type ImmerReducerClass (line 45) | interface ImmerReducerClass {
type ImmerReducerState (line 51) | type ImmerReducerState<T> = T extends {
type ImmerReducerFunction (line 60) | interface ImmerReducerFunction<T extends ImmerReducerClass> {
type ImmerActionCreator (line 68) | interface ImmerActionCreator<ActionTypeType, Payload extends any[]> {
type ActionCreators (line 78) | type ActionCreators<ClassActions extends ImmerReducerClass> = {
type ImmerAction (line 88) | type ImmerAction =
function isAction (line 106) | function isAction<A extends ImmerActionCreator<any, any>>(
function isActionFromClass (line 113) | function isActionFromClass<T extends ImmerReducerClass>(
function isActionFrom (line 138) | function isActionFrom<T extends ImmerReducerClass>(
type Reducer (line 145) | interface Reducer<State> {
function composeReducers (line 154) | function composeReducers<State>(
class ImmerReducer (line 171) | class ImmerReducer<T> {
method constructor (line 176) | constructor(draftState: Draft<T>, state: T) {
function removePrefix (line 182) | function removePrefix(actionType: string) {
constant KNOWN_REDUCER_CLASSES (line 189) | let KNOWN_REDUCER_CLASSES: typeof ImmerReducer[] = [];
constant DUPLICATE_INCREMENTS (line 191) | const DUPLICATE_INCREMENTS: {[name: string]: number | undefined} = {};
function setCustomNameForDuplicates (line 200) | function setCustomNameForDuplicates(immerReducerClass: typeof ImmerReduc...
function createImmerAction (line 248) | function createImmerAction(type: string, args: unknown[]): ImmerAction {
function getArgsFromImmerAction (line 263) | function getArgsFromImmerAction(action: ImmerAction): unknown[] {
function getAllPropertyNames (line 271) | function getAllPropertyNames(obj: object) {
function createActionCreators (line 282) | function createActionCreators<T extends ImmerReducerClass>(
function getReducerName (line 315) | function getReducerName(klass: {name: string; customName?: string}) {
function createReducerFunction (line 325) | function createReducerFunction<T extends ImmerReducerClass>(
function setPrefix (line 370) | function setPrefix(prefix: string): void {
function _clearKnownClasses (line 377) | function _clearKnownClasses() {
type WebpackModule (line 384) | interface WebpackModule {
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (55K chars).
[
{
"path": ".gitignore",
"chars": 39,
"preview": "/node_modules\r\n/package-lock.json\r\n/lib"
},
{
"path": ".prettierrc",
"chars": 73,
"preview": "{\n \"bracketSpacing\": false,\n \"trailingComma\": \"all\",\n \"tabWidth\": 4\n}\n"
},
{
"path": ".travis.yml",
"chars": 53,
"preview": "language: node_js\nnode_js:\n- 10\n- 8\nscript: npm test\n"
},
{
"path": ".vscode/settings.json",
"chars": 56,
"preview": "{\n \"typescript.tsdk\": \"node_modules/typescript/lib\"\n}"
},
{
"path": "CHANGELOG.md",
"chars": 54,
"preview": "See <https://github.com/epeli/immer-reducer/releases>\n"
},
{
"path": "LICENSE",
"chars": 1075,
"preview": "MIT License\n\nCopyright (c) 2018 Esa-Matti Suuronen\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "README.md",
"chars": 10206,
"preview": "# immer-reducer\n\nType-safe and terse reducers with Typescript for React Hooks and Redux using [Immer](https://immerjs.gi"
},
{
"path": "__dtslint__/generic-reducers.dtslint.ts",
"chars": 2791,
"preview": "import {\r\n ImmerReducer,\r\n createReducerFunction,\r\n createActionCreators,\r\n ImmerReducerState,\r\n} from \"../s"
},
{
"path": "__dtslint__/immer-reducer.dtslint.ts",
"chars": 6966,
"preview": "import {Action, createStore, bindActionCreators} from \"redux\";\r\n\r\nimport {\r\n ImmerReducer,\r\n createActionCreators,"
},
{
"path": "__tests__/immer-reducer.test.tsx",
"chars": 14245,
"preview": "import {\n ImmerReducer,\n createReducerFunction,\n createActionCreators,\n composeReducers,\n setPrefix,\n "
},
{
"path": "__tests__/use-reducer-integration.test.tsx",
"chars": 1149,
"preview": "import React from \"react\";\nimport {render, fireEvent, cleanup} from \"@testing-library/react\";\nimport {\n ImmerReducer,"
},
{
"path": "jest.config.js",
"chars": 284,
"preview": "module.exports = {\r\n moduleFileExtensions: [\"ts\", \"tsx\", \"js\"],\r\n transform: {\r\n \"^.+\\\\.(ts|tsx)$\": \"ts-jes"
},
{
"path": "package.json",
"chars": 1284,
"preview": "{\n \"name\": \"immer-reducer\",\n \"version\": \"0.7.13\",\n \"description\": \"\",\n \"main\": \"lib/immer-reducer.js\",\n \""
},
{
"path": "src/immer-reducer.ts",
"chars": 11406,
"preview": "import produce, {Draft} from \"immer\";\r\n\r\nlet actionTypePrefix = \"IMMER_REDUCER\";\r\n\r\n/** get function arguments as tuple "
},
{
"path": "tsconfig.build.json",
"chars": 247,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"exclude\": [\"__dtslint__\"],\n \"compilerOptions\": {\n \"sourceMap\": true,\n"
},
{
"path": "tsconfig.dtslint.json",
"chars": 60,
"preview": "{\r\n \"extends\": \"./tsconfig.json\",\r\n \"exclude\": []\r\n}\r\n"
},
{
"path": "tsconfig.json",
"chars": 314,
"preview": "{\n \"compilerOptions\": {\n \"module\": \"commonjs\",\n \"target\": \"es5\",\n \"noEmit\": true,\n \"jsx\":"
},
{
"path": "tslint.json",
"chars": 106,
"preview": "{\n \"rulesDirectory\": \"./node_modules/dtslint/bin/rules\",\n \"rules\": {\n \"expect\": true\n }\n}\n"
}
]
About this extraction
This page contains the full source code of the epeli/immer-reducer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (49.2 KB), approximately 12.0k tokens, and a symbol index with 124 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.