I've spent some time recently with this exact problem. While most answers I found suggested switching to Records, I am doing a large refactor and did not want to update all of the data structures in a rather large codebase.
After a lot of searching, I found this github issue that had the answer I was looking for:
https://github.com/facebook/immutable-js/issues/683#issuecomment-381089789 (see the last comment.)
Basically, you extend the basic immutable.Map interface to accept your type definition for your specific case.
For your specific case it would look like this:
import {User} from "../../models/user";
import {Map} from "immutable";
// The default Map interface accepts <K,V>: Key, Value.
// Build an interface that also accepts 'T': the shape of your data.
export interface IImmutableMap<T, K, V> extends Map<K, V> {
toJS(): T;
get<I extends keyof T>(key: I & K): T[I] & V;
set<S extends keyof T>(key: S & K, value: T[S] & V): Map<K, V>;
}
// Extend Map to define the shape of your data
export interface IState extends Map<string, any> {
user: User,
token: string,
};
// Pass the shape to your new interface to define a type.
export type TState = IImmutableMap<IState, string, any>;
// Update the type definition on initial state to your type.
const initialState: TState = Map<string, any>({
user: null,
token: null,
});
You can reuse this IImutableMap interface throughout your codebase by creating new interface and type definitions for each specific case.
If you need to create interfaces for other immutable.js data structures, the immutable docs will be invaluable: https://facebook.github.io/immutable-js/docs/#/
Here is a short blog that explains why you might choose not to use a Record data structure:
https://blog.mayflower.de/6630-typescript-redux-immutablejs.html