-
Notifications
You must be signed in to change notification settings - Fork 202
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
Question: does anyone know of a boilerplate generator for ng2-redux? #210
Comments
I'm not aware of a generator, no. It's an interesting idea though. Can you clarify what you mean about 'using reducers in a fully typed way'? Redux itself exposes types for actions and reducers. Generally I try to strongly type my reducers as follows: import { Action, Reducer } from 'redux';
export interface IPayloadAction<P> extends Action {
payload?: P;
}
export interface IMyState {
...
}
export const myReducer: Reducer<IMyState> = (
state: IMyState = INITIAL_STATE,
action: IPayloadAction<IPayload>) => {
// ...
} |
In a multi reducer application, I typically compose my state types too, e.g: interface IAppState {
state1?: IState1,
// ...
stateN?: IStateN,
}
export const rootReducer: Reducer<IAppState> = combineReducers({
state1: reducer1,
// ...
state2: reducerN,
};
///
constructor(private ngRedux: NgRedux<IAppState>) {} |
This works well for stores made up of plain objects. Getting it to play well with stores made out of constructs from ImmutableJS is a bit trickier because all your state ends up having type My colleague @danielfigueiredo has been experimenting with a solution to the problem of stong typings with Immutable.Record and Immutable.Map over here: https://github.com/rangle/typed-immutable-record |
Hello @SethDavenport, IPayLoadAction helps a bit indeed. However I do my reducer logic like this:
here the new object
cannot select the variables to set in a typed way. Of course, this part would not be possible to generate, as this is the logic itself. |
Hmm. I guess you're looking for a more strongly typed This is one of the ways TypedImmutableRecord tries to provide a better experience, by exposing a But since it's based on Immutable.Maps under the hood you may or may not be interested in it depending on your use case. |
@SethDavenport is right, spread objects can get tricky because you could combine an infinite number of types, my guess it would return an intersection of all. As long as you don't have any in the pack it would validate known properties. describe('test', () => {
it('intersection', () => {
function assign<A, B>(a: A, b: B): A & B {
return Object.assign({}, a, b);
}
interface MyA {
propA: string;
}
interface MyB {
propB: number;
}
const aB = assign<MyA, MyB>({propA: 'a'}, {propB: 1});
expect(aB.propA).to.not.null;
expect(aB.propB).to.not.null;
});
}); Not sure if that is what you want =] Combining an unknown number of types I'm not sure if it is possible. |
Also found this that might be interesting |
Thanks for the links @danielfigueiredo, seems like lots of people are interested in this topic. By default, the typings for What would be nice is a version of Fortunately it looks like TypeScript 1.8 introduced features that make this possible: https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#type-parameters-as-constraints. Unfortunately the example given there is mutating, whereas we want to construct a new object of the master type. |
However we can use function tassign<T extends U, U>(target: T, source: U): T {
return Object.assign({}, target, source);
}
let x = { a: 1, b: 2, c: 3, d: 4 };
tassign(x, { b: 10, d: 20 }); // OK
// Error: Property 'e' is missing in type
// '{ a: number; b: number; c: number; d: number; }
tassign(x, { e: 0 }); I'm only having issues making it variadic, but that might not be strictly needed in this case. |
Putting it all together into a typesafe reducer would look like this: import { Reducer, Action } from 'redux';
const INITIAL_STATE: ITripState = {
loading: false,
trips: null
};
export interface ITripState {
loading: boolean;
trips: string[];
}
export const tripReducer: Reducer<ITripState> = (
state: ITripState = INITIAL_STATE,
action: Action) => {
switch(action.type) {
case TRIP_ACTIONS.QUERY_TRIPS_ASYNC:
if (state.loading) return state;
return tassign(state, {
loading: true,
trips: null,
foo: 'sdas'
});
}
} |
@e-schultz would it be worth packaging the |
@SethDavenport, @danielfigueiredo thanks for your deep discussion on this. |
@SethDavenport thinking about this reducer and state use case you're absolutely right. function tassign<T extends U, U>(target: T, ...source: U[]): T {
return Object.assign({}, target, ...source);
}
let x = { a: 1, b: 2, c: 3, d: 4 };
tassign(x, { b: 10, d: 20 }); |
@danielfigueiredo yeah unfortunately the variadic formulation doesn't work (I tried that earlier). Just try doing this: let x = { a: 1, b: 2, c: 3, d: 4 };
tassign(x, { b: 10 }, {d: 20 }); The issue is that here we have 3 types. In general you'd need N types for this to work. |
@SethDavenport gahh you're right, and makes sense now |
@istvanszoboszlai to be honest I don't have much experience with code generation. I prefer to type it out myself, helps me remember what I did :) What tools have you been using? |
Closing this issue for now; code generation isn't something we have much experience with. If someone wants to implement code generation as an external module, I'm happy to advise. If such a module could also work for ngrx/store that would be even better :) |
Of course, the situation is not that bad, but would be nice to make it even easier to use by generating actions and reducer frames.
Mostly because it is easy to mistype variables, and AFAIK the reducer cannot be used in a fully typed way.
The text was updated successfully, but these errors were encountered: