esamattis / immer-reducer

Type-safe and terse reducers with Typescript for React Hooks and Redux
http://npm.im/immer-reducer
MIT License
225 stars 15 forks source link

export interface ImmerReducerFunction #46

Closed hcharley closed 4 years ago

esamattis commented 4 years ago

This fixes #42?

hcharley commented 4 years ago

Not completely, and sorry for the crpytic PR. This partially helped me.

I think the key is that either this file should export all of its contents, or have no exports--treating it as a declaration file. There may be some way to namespace it too. Truthfully i've never had to maintain a library before, so I'm probably not too much of a help here.

What I ultimately did just to stay focused on what I was trying to solve for my project, was to simply copy this file for my own uses, tweaking where I found it helpful for my usecase. Honestly, haven't even done a diff on this, so not sure which ones I left alone and which ones I modified:

import {
  ActionCreators,
  ImmerReducerClass,
  ImmerReducerState,
} from 'immer-reducer';
import { Dispatch } from 'react';

/** 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>
>;

export interface ImmerReducerFunction<T extends ImmerReducerClass> {
  (
    state: ImmerReducerState<T> | undefined,
    action: ReturnTypeUnion<ActionCreators<T>>
  ): ImmerReducerState<T>;
}

export type ImmerReducerFunctionObj<T extends ImmerReducerClass> = {
  state: ImmerReducerState<T> | undefined;
  action: ReturnTypeUnion<ActionCreators<T>>;
};

export type ImmerReducerFunctionObjReact<T extends ImmerReducerClass> = {
  state: ImmerReducerState<T> | undefined;
  dispatch: Dispatch<ReturnTypeUnion<ActionCreators<T>>>;
  actions: ActionCreators<T>;
};

export interface ImmerActionCreator<ActionTypeType, Payload extends any[]> {
  readonly type: ActionTypeType;

  (...args: Payload): {
    type: ActionTypeType;
    payload: FirstOrAll<Payload>;
  };
}

// export type ImmerReducerState<T> = T extends {
//   prototype: {
//       state: infer V;
//   };
// }

And my usecase:

import {
  ImmerReducerFunction,
  ImmerReducerFunctionObj,
  ImmerReducerFunctionObjReact,
} from './immer';
import { MyAppReducer } from './MyAppReducer';

export interface IMyAppState {
  name: string;
  todos: Todo[];
  featuredTodo: Todo;
  enabled: boolean;
}

export interface IMyAppActionType<
  ActionName extends string,
  ActionData extends Partial<IMyAppState>
> {
  type: ActionName;
  data: ActionData;
}

export type MyAppReducerObj = ImmerReducerFunctionObj<
  typeof MyAppReducer
>;
export type MyAppReducerReact = ImmerReducerFunctionObjReact<
  typeof MyAppReducer
>;
export type MyAppReducerState = MyAppReducerObj['state'];
export type MyAppReducerAction = MyAppReducerObj['action'];

export type MyAppReducerFunction = ImmerReducerFunction<
  typeof MyAppReducer
>;
esamattis commented 4 years ago

Have you tried setting "declaration": false, to your tsconfig.json? Unless you are doing a library with .d.ts exports it should be ok.

Gonna merge this neither way as it won't hurt anything.

esamattis commented 4 years ago

Released https://github.com/esamattis/immer-reducer/releases/tag/v0.7.10

hcharley commented 4 years ago

Thank you @esamattis !