Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Types: derive actions type from the symbiotes type #28

Open
Finesse opened this issue Jul 17, 2019 · 7 comments

Comments

@Finesse
Copy link

commented Jul 17, 2019

Issuehunt badges

Current TypeScript implementation is not convenient because I have to describe the actions 2 times: the type and the implementation. Moreover, the createSymbiote type doesn't fully check that the actions type and implementation match.

// 1st actions description
interface Actions {
  foo(): Action;
  bar(arg: string): Action
}

// 2nd actions description
const symbiotes = {
  foo(state: State) {
    return {...state, value: 'foo'};
  },
  bar(state: State, value: number) { // The argument type mismatch is not checked
    return {...state, value: `bar ${value}`};
  }
};

const {actions, reducer} = createSymbiote<State, Actions>({value: 'Hi'}, symbiotes);

The single source of truth principle is broken.

I've made a draft of typing for createSymbiote that solves the problem. The function type derives the returned actions type from the input symbiotes type automagically:

export interface NamespaceOptions<State> {
  namespace?: string
  defaultReducer?: (prevState: State, action: Action) => State
  separator?: string
}

export type Symbiote<State> = (state: State, ...payload: any[]) => State

export type Reducer<State> = (state: State, action: Action) => State

export type Actions<Symbiotes> = {
  [Key in keyof Symbiotes]: Symbiotes[Key] extends Function
    ? ActionCreator<Symbiotes[Key]>
    : Actions<Symbiotes[Key]>
}

export interface Action<Payload = any> {
  type: string
  payload?: Payload
}

// Inspired by the built-in Parameters type
type ParametersFromSecond<T> = T extends (_: any, ...args: infer P) => any ? P : never;

type ActionCreator<Symbiote> = (...args: ParametersFromSecond<Symbiote>) => Action;

export function createSymbiote<State, Symbiotes>(
  initialState: State,
  actionsConfig: Symbiotes,
  namespaceOptions?: string | NamespaceOptions<State>,
): {
  actions: Actions<Symbiotes>
  reducer: Reducer<State>
}

The example can be converted with these types:

// Only 1 actions description
const symbiotes = {
  foo(state: State) {
    return {...state, value: 'foo'};
  },
  bar(state: State, value: number) {
    return {...state, value: `bar ${value}`};
  }
};

const {actions, reducer} = createSymbiote({value: 'Hi'}, symbiotes);

actions.bar('cocktail'); // TS error, a number is expected

I can make a PR if you agree. It'll be a breaking change for the typing.


IssueHunt Summary

Backers (Total: $5.00)

Become a backer now!

Or submit a pull request to get the deposits!

Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

@Finesse

This comment has been minimized.

Copy link
Author

commented Aug 8, 2019

@sergeysova What can I do to help?

@sergeysova

This comment has been minimized.

Copy link
Owner

commented Aug 8, 2019

@Finesse You can fund the issue. https://issuehunt.io/r/sergeysova/redux-symbiote/issues/28
I don't know typescript to solve this issue

@issuehunt-app

This comment has been minimized.

Copy link

commented Aug 8, 2019

@sergeysova has funded $5.00 to this issue.


@Finesse

This comment has been minimized.

Copy link
Author

commented Aug 8, 2019

@sergeysova What about me to write the code? I know TypeScript (see my draft).

@sergeysova

This comment has been minimized.

Copy link
Owner

commented Aug 8, 2019

@Finesse PR Welcome!

@Finesse

This comment has been minimized.

Copy link
Author

commented Aug 8, 2019

Is the types backward compatibility required? I can make a simple solution like in my draft that changes the types API. Or I can try to make backward compatible types, but I think it may be impossible.

@sergeysova

This comment has been minimized.

Copy link
Owner

commented Aug 8, 2019

You can implement types without backward compatibility

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.