If you find yorself using immutable.js
maps more and more like native objects, it could be that a library like immer is more suitable.
Otherwise, here's an example of a complete version, including filter
etc:
import { fromJS, Map } from 'immutable'
type Modify<T, R> = Omit<T, keyof R> & R;
type AnyObject = Record<string, any>
export const TypedMap = <T extends AnyObject>(val: T) =>
fromJS(val) as any as TypedMap<T>
export type TypedMap<T extends AnyObject> =
Modify<Map<keyof T, T[keyof T]>, TypedMapBase<T>>
export interface TypedMapBase<T extends AnyObject> {
set: <K extends keyof T>(key: K, value: T[K]) => this
get: <K extends keyof T>(key: K) => T[K]
delete: <K extends keyof T>(key: K) => boolean
deleteAll<K extends keyof T>(keys: Iterable<K>): this
remove<K extends keyof T>(key: K): this
removeAll<K extends keyof T>(keys: Iterable<K>): this
update<K extends keyof T>(key: K, notSetValue: T[K], updater: (value: T[K]) => T[K]): this
update<K extends keyof T>(key: K, updater: (value: T[K]) => T[K]): this
merge<KC, VC, K extends keyof T>(
...collections: Array<Iterable<[KC, VC]>>
): Map<K | KC, T[K] | VC>
merge<C, K extends keyof T>(
...collections: Array<{ [key: string]: C }>
): Map<K | string, T[K] | C>
concat<KC, VC, K extends keyof T>(
...collections: Array<Iterable<[KC, VC]>>
): Map<K | KC, T[K] | VC>
concat<C, K extends keyof T>(
...collections: Array<{ [key: string]: C }>
): Map<K | string, T[K] | C>
mergeWith<K extends keyof T>(
merger: <K extends keyof T>(oldVal: T[K], newVal: T[K], key: K) => T[K],
...collections: Array<Iterable<[K, T[K]]> | { [key: string]: T[K] }>
): this
mergeDeep<K extends keyof T>(
...collections: Array<Iterable<[K, T[K]]> | { [key: string]: T[K] }>
): this
mergeDeepWith<K extends keyof T>(
merger: (oldVal: unknown, newVal: unknown, key: unknown) => unknown,
...collections: Array<Iterable<[K, T[K]]> | { [key: string]: T[K] }>
): this
map<M, K extends keyof T>(
mapper: (value: T[K], key: K, iter: this) => M,
context?: unknown
): Map<K, M>
mapKeys<M, K extends keyof T>(
mapper: (key: K, value: T[K], iter: this) => M,
context?: unknown
): Map<M, T[K]>
mapEntries<KM, VM, K extends keyof T>(
mapper: (
entry: [K, T[K]],
index: number,
iter: this
) => [KM, VM],
context?: unknown
): Map<KM, VM>
flatMap<KM, VM, K extends keyof T>(
mapper: (value: T[K], key: K, iter: this) => Iterable<[KM, VM]>,
context?: unknown
): Map<KM, VM>
filter<F extends T[K], K extends keyof T>(
predicate: (value: T[K], key: K, iter: this) => value is F,
context?: unknown
): Map<K, F>
filter<K extends keyof T>(
predicate: (value: T[K], key: K, iter: this) => unknown,
context?: unknown
): this
flip<K extends keyof T>(): Map<T[K], K>
}
And a partial extension:
export const PartialTypedMap = <T extends AnyObject>(val: Partial<T>) =>
fromJS(val) as any as PartialTypedMap<T>
export type PartialTypedMap<T extends AnyObject> = Modify<TypedMap<T>, PartialTypedMapBase<T>>
interface PartialTypedMapBase<T extends AnyObject>{
get: <K extends keyof T>(key: K) => T[K] | undefined
update<K extends keyof T>(key: K, updater: (value: T[K]|undefined) => T[K]): this
mapEntries<KM, VM, K extends keyof T>(
mapper: (
entry: [K, T[K]],
index: number,
iter: this
) => [KM, VM] | undefined,
context?: unknown
): Map<KM, VM>
}