Skip to content

Using Immutable.js’s Maps with TypeScript #683

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

Closed
OliverJAsh opened this issue Oct 29, 2015 · 35 comments
Closed

Using Immutable.js’s Maps with TypeScript #683

OliverJAsh opened this issue Oct 29, 2015 · 35 comments

Comments

@OliverJAsh
Copy link

OliverJAsh commented Oct 29, 2015

I'm using immutable.js with TypeScript. I want a typed immutable object, e.g. an object called Person with an interface of: (name: string, age: number).

Ideally I could construct a Person and it will be an instance of Immutable.Map:

interface Person {
    name: string;
    age: number
}

var person = Person({ name: 'bob', age: 5 })
person.name // => 'bob'
var newPerson = person.set('name', 'harry');
person.name // => 'bob'
newPerson.name // => 'harry'

Is this possible? Sorry if my terminology is not entirely clear!

Thanks 😄

@dimitrikochnev
Copy link

Duplicate of #166 and #341.

@OliverJAsh
Copy link
Author

I'm currently following advice from #341 (comment) but this involves overwriting the type definitions. I would also prefer a way to do this without requiring defaults to be set—ideally TypeScript should throw an error if you tried to create an object missing a property.

@dimitrikochnev
Copy link

To be honest I spent a few days now and a month ago trying to find a solution that would fit all the points including a record inheritance with custom methods, attributes checking and properties accessing using a dot-notation, but I still haven't found it. This feature mixed with a record generic type definitions you mentioned above could solve most of them microsoft/TypeScript#2225. But there is no progress. @leebyron Could you help us?

@OliverJAsh OliverJAsh changed the title Using Immutable.js’s Maps/Records with TypeScript Using Immutable.js’s Maps with TypeScript Apr 16, 2016
@OliverJAsh
Copy link
Author

One suggestion is to use Immutable’s Records, but in this case I don't want to allow default values.

@emirotin
Copy link

@OliverJAsh if records work for you setting undefined defaults should be effectively the same as not setting them at all. Am I wrong?

@OliverJAsh
Copy link
Author

Yes, correct, it's just boilerplate really.

On Tue, 28 Jun 2016 at 19:54 Eugene Mirotin notifications@github.com
wrote:

@OliverJAsh https://github.com/OliverJAsh if records work for you
setting undefined defaults should be effectively the same as not setting
them at all. Am I wrong?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#683 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AA4QCbtrhyPnmGe85DfJFGg4k-nqLyPoks5qQW3IgaJpZM4GYB74
.

@aalpgiray
Copy link

aalpgiray commented Sep 7, 2016

I found a class to get prop name as string array, then implemented a SetValue Method this has some rough edges.

export class NavigableObject<T>{
    constructor(private obj: T, private path: string[] = []) { }

    To<R>(p: (x: T) => R): NavigableObject<R> {

        let propName = this.getPropName(p)

        if (propName) {
            return new NavigableObject<R>(
                p(this.obj),
                this.path.concat(propName)
            );
        } else {
            return new NavigableObject<R>(
                p(this.obj),
                this.path
            );
        }
    }

    getPath() {
        return this.path;
    }


    private static propertyRegEx = /\.([^\.;]+);?\s*\}$/;

    private getPropName(propertyFunction: Function) {
        let value = NavigableObject.propertyRegEx.exec(propertyFunction.toString())
        if (value)
            return value[1];
    }
}

function NavigatableRecordFactory<X>(defaultValues: X, name?: string) {
    abstract class NavigatableRecord<P extends NavigatableRecord<P>> extends Record(defaultValues, name) {
        SetValue<T>(fn: (x: NavigableObject<P>) => NavigableObject<T>, value: T) {
            return this.setIn(fn(new NavigableObject<any>(this)).getPath(), value)
        }
    }
    return NavigatableRecord;
}

interface IUSER {
    Name: string;
    Age: number;
}

export class USER extends NavigatableRecordFactory<IUSER>({
    Name: "Simy the bothless",
    Age: 27,
})<USER> implements IUSER {
    Name: string;
    Age: number;
}

and then use it like

state.Name // works

state.SetValue(t => t.To(q => q.Name), "test string") // typecheks
state.SetValue(t => t.To(q => q.Name), 123) // error

it also works with nested properties

somenestedImmutable.SetValue(t =>t.To(q => q.Date).To(q => q.Time), 213213123)

but cant get it work without needing to implement method in child class ,
little help would be nice if it is possible :)

Typescript v.2.1.0-dev20160805

@born2net
Copy link

born2net commented Jan 23, 2017

Hi Guys,
its 2017, so can we Type a Map now with immutablejs and TypeScript? Specifically enforce the keys.
Posted here: http://stackoverflow.com/questions/41798302/how-do-you-type-an-immutablejs-map-keys
but no one seems to know, odd :/

regards

Sean

Checkout the Ultimate Angular 2 Boorstrap App: @ http://ng2.javascriptninja.io
Source@ https://github.com/born2net/ng2Boilerplate

@leebyron
Copy link
Collaborator

leebyron commented Mar 8, 2017

Fixed in master, will be released soon

@leebyron leebyron closed this as completed Mar 8, 2017
@born2net
Copy link

born2net commented Mar 8, 2017

looking fwd to it...
what release will it land on?

@born2net
Copy link

born2net commented May 4, 2017

was this released already?

@born2net
Copy link

born2net commented May 4, 2017

fyi I tried with latest release immutable beta rc and no luck


interface ITimelineState {
    zoom: number;
    duration: number;
    channels: Array<IChannels>;
    outputs: Array<IOutputs>;
    items: Array<IItem>;
}

let state: Map<any, ITimelineState> = Map({ // <<<< ERROR
        zoom: 1,
        duration: 500,
        channels: [],
        outputs: [],
        items: []
    });

@jacek213
Copy link

@leebyron Can we see any example of usage?

@born2net
Copy link

ya same... really need an example please

@xugao
Copy link

xugao commented May 16, 2017

Is this released? An example would be great!

@born2net
Copy link

any news on this?

@agaurav
Copy link

agaurav commented Jun 6, 2017

@leebyron can you please post the commits related to fix?

@DonikaV
Copy link

DonikaV commented Jul 6, 2017

Hello from july 6

@born2net
Copy link

Hello from July 26th

@born2net
Copy link

@leebyron any news on this? would love to be able to Type Maps

@odiseo42
Copy link

@leebyron Hello from October 2017! an example would be much appreciated :)

@born2net
Copy link

same!

@leebyron
Copy link
Collaborator

leebyron commented Oct 11, 2017

You may want to look to the TypeScript documentation for examples of how to use TypeScript.

Map is defined in terms of the type of the keys it contains and the type of the values it contains, respectively. It appears as: Map<KeyType, ValueType> in TypeScript.

For example, if you wanted a Map with string keys and number values, you'd write Map<string, number>. For the example above Map<any, ITimelineState>, this declares a Map that will accept any value as a key, and instances of ITimelineState as values.

For cases where the type of value at each key will be different depending on the key, then Map is likely the wrong choice. Maps are intended for homogeneous collections. For heterogeneous collections with a fixed known set of keys, Record is a better fit.

You can see examples of using Record in the Immutable.js documentation here: http://facebook.github.io/immutable-js/docs/#/Record

@odiseo42
Copy link

odiseo42 commented Oct 25, 2017

An example usage for those wondering (following this post), where you want to type check every property accesed via get:

interface IProduct {
  price: number;
  description: string;
}

What you do is first to declare a typing interface for your Product as it were a Plan Old JavaScript object. Then you proceed to "wrap" it extending Immutable.Map:

interface IImmutableProduct extends Immutable.Map<string, any> {
   toJS(): IProduct;
   get<K extends keyof IProduct>(key: K): IProduct[K];
}

Thus you'll have type checking over the getter functions of Product, in this case .get('price') and .get('description').

@born2net
Copy link

That's great, can't wait to test it

@Saul-Mirone
Copy link

Saul-Mirone commented Apr 13, 2018

Hi there, I make some attempt and get a solution with out any like this:

interface ImMap<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>;
}
//test
interface StringMap {
  [propName: string]: string;
}
type ImStringMap = ImMap<D.StringMap, string, string>;

interface Attach {
  val: string;
  type: number;
  sh1?: string;
  keyName?: string;
}
type ImAttach = ImMap<D.Attach, string, string | number>;

It works well for me.

@bohdanbirdie
Copy link

@odiseo42 @Saul-Mirone thanks for the examples!
Any proposals for getIn method?

@born2net
Copy link

born2net commented Jul 29, 2018

seems to work:


interface IMAPTScannerTabSelection extends Map<string, any> {
    toJS(): IScannerTabSelection;
    get<K extends keyof IScannerTabSelection>(key: K): IScannerTabSelection[K];
}



const scannerTabSelection = {
    mainTab: {
        value: EnumScannerMainTab.MAINTAB_BLOCKS
    },
    switchTab: {
        value: EnumScannerSwitchTab.SWITCH_BROWSE_BLOCKS22
    }
};

var a:IMAPTScannerTabSelection = Map(scannerTabSelection);
var b = a.get("switchTab")
console.log(b);

and got auto completion and error detection in Angular 5 / TS

@mmakrzem
Copy link

Can anyone help answer this question: https://stackoverflow.com/questions/52824312/how-to-use-immutablejs-map-with-typescript

I can't figure out how to define my ImmutableMap in TypeScript that contains a List of (child) ImmutableMaps.

@kvedantmahajan
Copy link

@odiseo42 What if I need to access the full object. Any workaround for that?

@JustFly1984
Copy link

@kushalmahajan this project more dead than alive, try immer instead

@kvedantmahajan
Copy link

@JustFly1984 Thanks for the suggestion but I cannot just try new package. Already deep into it.

@JustFly1984
Copy link

Well, I was deep in immutable.js with 5 projects,until we had to refactor all the projects to typescript, it happened that there is no support :/ we had to refactor anyway, and used immer as 100% typescript support. Also this library is unmaintained for way too long and there is some bugs which killing all the benefits. For example if you have a list of strings more than 32 items, it will iterate without an order :/

@DonikaV
Copy link

DonikaV commented Apr 30, 2020

Guys in 2020 and with ES6 you don't need immutable, IMHO :)

@JustFly1984
Copy link

JustFly1984 commented Apr 30, 2020

@DonikaV
We need some kind of data structures with persistence properties, cos immutability is not enough for performance optimized algorithms.

Immutability by itself can be provided with Object.freeze() recursively or shallow. Or simply use immer library. But persistence by itself provides huge performance boost

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests