Related issue: https://github.com/ctrlplusb/easy-peasy/issues/117
I'm using "easy-peasy": "2.3.0" and "typescript": "3.3.4000".
I have the following store, which produces a TS "error":
interface IMyStoreModel<T extends IMyInterface> {
myMap: {
[id: string]: T;
};
values: Select<IMyStoreModel<T>, Array<T>>;
}
const generateModel = <T extends IMyInterface>() => {
const myStore: IMyStoreModel<T> = {
myMap: {},
values: select(state =>
Object.keys(state.map)
// 馃憞 error
.map(key => state.myMap[key])),
}
return myStore;
}
If I define IMyStoreModel without using generic types, the error disappears:
// works
interface IMyStoreModel {
myMap: {
[id: string]: IMyInterface;
};
values: Select<IMyStoreModel, Array<IMyInterface>>;
}
I could also cast state.myMap to its correct type - but that gets quite ugly.
Here is the exact error message produced:
(property) myMap: Compact<{ [K in (T extends Select
| Reducer ? string : never) | (T extends Select | Reducer ? number : never) | Exclude Element implicitly has an 'any' type because type 'Compact<{ [K in (T extends Select | Reducer ? string : never) | (T extends Select | Reducer ? number : never) | Exclude
Hey @jmyrland,
Can you try upgrade to the latest version of easy-peasy? I believe this is an older issue with the bundled definitions that has since been fixed 馃憤
It's a non-breaking upgrade so you should be fine. 馃榿
Oh wait! My bad you are on the latest version. 馃槄
Ummm, I'll review this asap. 馃憤
Yeah I saw your original issue and upgraded to 3.4.0. It's only an issue with generic types. Not a huge issue, and it's easy to work around. However, would love to see a fix for this 馃榿
I started out using the Map<key, value> type, but this had sort of the same issues if I remember correctly. Is the Map type supported for state properties or do you see any issues with this approach ? 馃
Phew, that was tricky as hell. It's so hard to isolate index signatures when mapping over types. I added your case to the typescript test files and my fix appears to be working. I've published this an alpha fix for now. I would really appreciate if you could test them out and feedback. I'm particularly interested to hear if any regressions were introduced. I have a fairly decent set of tests for the typescript defs so hopefully not. 馃
npm i [email protected]

I started out using the Map
type, but this had sort of the same issues if I remember correctly. Is the Map type supported for state properties or do you see any issues with this approach ?
Yeah, I don't recommend that approach as it's not a JSON serialisable structure. You'll definitely want the pure object form. 馃憤
Wow, did not expect a resolution this quickly! Well done! 馃榿
I will give the alpha a shot and let you know how it works out in my case.
Yeah, I don't recommend that approach as it's not a JSON serialisable structure. You'll definitely want the pure object form. 馃憤
Ah, as I suspected. I have seen this when I've modeled my stores with class instances. This might be a good thing to document in relation to "how to model stores" and advice to use serializable types 馃憤
Sweet! Installed the alpha just now - and the error is gone! 馃憣 And I don't have to type cast results with this change! 馃帀
The current build of my project is currently unstable (failing tests due to breaking changes) - so I don't know for certain if this change has any side effects - but as far as I can tell, it works as it should!
FYI, [email protected] released, and it doesn't like this fix. Gets caught in infinite inference resolutions. I'm gonna try and figure it out prior to it becoming full patch.
Oh, we're a few minors behind I see 馃槄 Hopefully there are no breaking changes...
Keep me posted and I will test it with the new version of TS as well 馃槃
No worries, TBH I have been sticking to 3.3.4000 as >=3.4 have introduced performance regressions with definitions of libraries such as styled-components. :)
Hey @jmyrland, I've released a new version which I believe fixes this issue across all the most recent versions of TS. I would really appreciate it if you could use this version and feedback to me. I'll likely publish this to master if you give me a thumbs up.
npm i [email protected]
Testing ATM - it seems that accessing the state is working, but setting the state is not. (TS 3.3.4000)
I have updated your test case to account for this as well:
interface IMyInterface {
name: string;
}
interface IMyInterfaceMap<T extends IMyInterface> {
[id: string]: T;
}
interface IMyStoreModel<T extends IMyInterface> {
myMap: IMyInterfaceMap<T>;
values: Select<IMyStoreModel<T>, Array<T>>;
setState: Action<IMyStoreModel<T>, IMyInterfaceMap<T>>
}
const generateModel = <T extends IMyInterface>(): IMyStoreModel<T> => {
return {
myMap: {},
values: select(state => {
return Object.keys(state.myMap).map(key => state.myMap[key]);
}),
setState: action((state, newMap) => {
// 馃憞 error
state.myMap = newMap;
})
};
};
interface IMyExtendedInterface extends IMyInterface {
extended: boolean;
}
const store = createStore(generateModel<IMyExtendedInterface>());
const foo = Object.values(store.getState().myMap);
foo[0].name = 'bob';
foo[0].extended = true;
store.getState().myMap['foo'] = { name: 'bob', extended: true };
store.getState().myMap['foo'].name + 'foo';
Error message:
Type 'IMyInterfaceMap
' is not assignable to type '{ [key: string]: T; } & { [P in keyof Pick | Listen | Thunk ? string : never>, T extends Select | Reducer<...> ? string : never> | Exclude<...>]: Pick<...>[K]; } & Pick<...>>, { [K in keyof Compa...'.
Also tried upgrading TS to 3.5.1 - but the issue is still present there 馃槩
Great thanks I will add this to the test cases 馃憤
@jmyrland - 3rd times a charm.
npm i [email protected]
It seems to be working, but there is something wierd with selectors - it does not seem to be part of the state object? 馃
Updated testcase for you:
interface IMyInterface {
name: string;
}
interface IMyInterfaceMap<T extends IMyInterface> {
[id: string]: T;
}
interface IMyStoreModel<T extends IMyInterface> {
myMap: IMyInterfaceMap<T>;
values: Select<IMyStoreModel<T>, Array<T>>;
names: Select<IMyStoreModel<T>, string>;
setState: Action<IMyStoreModel<T>, IMyInterfaceMap<T>>
}
const generateModel = <T extends IMyInterface>(): IMyStoreModel<T> => {
return {
myMap: {},
values: select(state => {
return Object.keys(state.myMap).map(key => state.myMap[key]);
}),
names: select(state => {
// 馃憞 error
return state.something.map(x => x.name);
}),
setState: action((state, newMap) => {
state.myMap = newMap;
})
};
};
interface IMyExtendedInterface extends IMyInterface {
extended: boolean;
}
const store = createStore(generateModel<IMyExtendedInterface>());
const foo = Object.values(store.getState().myMap);
foo[0].name = 'bob';
foo[0].extended = true;
store.getState().myMap['foo'] = { name: 'bob', extended: true };
store.getState().myMap['foo'].name + 'foo';
Error:
Property 'something' does not exist on type 'Compact<{ myMap: IMyInterfaceMap
; } & Compact<{} & {}>>'.
I'm guessing selectors is supposed to be part of the first Compact-thingy?
Hey @jmyrland
I updated your case a bit to what I think you were trying to describe:
/* eslint-disable */
import { createStore, select, Select, action, Action } from 'easy-peasy';
interface IMyInterface {
name: string;
}
interface Person extends IMyInterface {}
interface IMyStoreModel<T extends IMyInterface> {
myMap: Record<string, T>;
setMyMap: Action<IMyStoreModel<T>, Record<string, T>>;
values: Select<IMyStoreModel<T>, Array<T>>;
names: Select<IMyStoreModel<T>, Array<string>>;
}
const generateModel = <T extends IMyInterface>(): IMyStoreModel<T> => {
return {
myMap: {},
setMyMap: action((state, payload) => {
state.myMap = payload;
}),
values: select(state => {
return Object.keys(state.myMap).map(key => state.myMap[key]);
}),
names: select(state => state.values.map(x => name)),
};
};
const store = createStore(generateModel<Person>());
const foo = Object.values(store.getState().myMap);
foo[0].name = 'bob';
store.getState().myMap = {
foo: { name: 'bob' },
};
store.getState().values.map(x => x.name);
store.getState().myMap['foo'] = { name: 'bob' };
store.getState().myMap['foo'].name + 'foo';
Interestingly, this works in [email protected], but not in [email protected] (and I am assuming you are still using [email protected] - so it doesn't work in that version either).
Ah yes, I was a little too quick there 馃槗
I meant the following select (almost the one you got)
// selector
names: select(state => state.values.map(x => x.name)),
// usage:
store.getState().names
So in my case I get an error for values, basically stating that this property does not exist on state (in the selector). We are still on version 3.3.4000.
We are using CRA, and it seems we are bound to a specific TS version. When I tried upgrading to 3.5.1, I got some "unstable" warnings. It may be that 3.4.5 is supported - but I would have to test it.
Umm, it seems like I only get the error in my IDE - but running npm start produces no error.. 馃
I'll have to check this out some more to see if I can spot why this is happening.
Now it's working with [email protected]! 馃帀 馃帀 馃帀
No errors - no warnings, all good! 馃槃 And all test passing! Great job! 馃憤
also [email protected] worked like a charm. Seems like there was some issues with [email protected] - even got errors with useStore hooks. All kinks gone after upgrading to alpha 3 馃憤
It's also compatible with [email protected] FYI
Thanks so much for helping me check this change!
My pleasure! Looking forward to the patch 馃憤
Most helpful comment
Also tried upgrading TS to
3.5.1- but the issue is still present there 馃槩