Skip to content
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

Typescript typings #108

Open
jonaskello opened this issue Mar 7, 2016 · 45 comments
Open

Typescript typings #108

jonaskello opened this issue Mar 7, 2016 · 45 comments

Comments

@jonaskello
Copy link

I think this is a nice lib! Unfortunately my project uses typescript so I cannot use it without typings. I did some searches but came up empty so I don't think it exists? The closes I found was this question on stack overflow.

So it seems some ppl are already working on typings for this lib. I think it would be nice if we could co-ordinate the work here.

@tyoh
Copy link

tyoh commented Mar 8, 2016

+1

@zivni
Copy link

zivni commented Mar 9, 2016

Well, this my first try. but it isn't very good:

declare module seamlessImmutable {
    interface ImmutableCommonMethods<T>{
        setIn?<U extends T>(keys: Array<string|number>, value: any): T | U;
        merge?<U>(obj: U): T & U;
    }

    interface ImmutableObjectMethods<T> extends ImmutableCommonMethods<T> {
        set?<U extends T>(key: string, value: any): U;
        asMutable?(): T;
        without?<U>(key: string): U;
        without?<U>(keys: string[]): U;
        without?<U>(...args: string[]): U;
        without?<U>(keyFunction: (value: any, key: string) => boolean): U;
    }

    interface ImmutableArrayMethods<T> extends ImmutableCommonMethods<T> {
        set?<T>(index: number, value: any): Array<T>;
        asMutable?(): Array<T>;
        asObject?<U>(toKeyValue: (item: T) => Array<Array<any>>): U;
        flatMap?<U>(mapFunction: (item: T) => Array<U>)

    }
}
    declare function SI<T>(obj: T, options?): T & seamlessImmutable.ImmutableObjectMethods<T>;
    declare function SI<T>(obj: Array<T>, options?): Array<T> & seamlessImmutable.ImmutableArrayMethods<T>;

@rtfeldman
Copy link
Owner

Cool! Seems like a useful addition. 😺

@panKt
Copy link

panKt commented Mar 18, 2016

+1, would be great to have it here!

@jonaskello
Copy link
Author

Here is what I have come up with so far. It only allows usage of the lib in typescript. It does not provide compile time error for accessing read-only data.

seamless-immutable.d.ts:

declare module 'seamless-immutable' {

  type Immutable = {
    (obj:any, options?:any):any;
    isImmutable(target:any):boolean;
    ImmutableError(message:string):Error;
  }

  var Immutable:Immutable;
  export = Immutable;

}

Usage:

import * as Immutable from 'seamless-immutable';

var array = Immutable(["totally", "immutable", {hammer: "Can’t Touch This"}]);
array[1] = "I'm going to mutate you!"

The last line in usage will give a runtime error. It would of course be much better if it gave a compile time error. @zivni your typings look interesting but I am not sure how to use them?

@Ciantic
Copy link

Ciantic commented Mar 20, 2016

I'm looking for this as well, but my idea is a bit like the last one above I think.

Use seamless-immutable as a wrapper, and then use lodash or other things to "mutate" and after that freeze again with Immutable.

This way I don't need any API basically more than it's just a function that returns the same thing it took in I think...

That is the theory at least.

@Ciantic
Copy link

Ciantic commented Mar 20, 2016

@jonaskello I think you should define with generic and return with it too:

declare module "seamless-immutable" {

  type Immutable = {
    <T>(obj: T, options?: any): T;
    isImmutable(target: any): boolean;
    ImmutableError(message: string): Error;
  }

  var Immutable: Immutable;
  export = Immutable;
}

So that it type checks / autocompletes the object it returns.

@jonaskello
Copy link
Author

Here is a version that combines the additions @Ciantic made with the methods defined by @zivni. It enables typed usage of the original type plus the extra methods that SI provides. I think what is left is to remove the mutation methods that SI bans. However I am not sure how to solve that or if it is even possible?

declare module 'seamless-immutable' {

  interface ImmutableCommonMethods<T>{
    setIn?<U extends T>(keys: Array<string|number>, value: any): T | U;
    merge?<U>(obj: U): T & U;
  }

  interface ImmutableObjectMethods<T> extends ImmutableCommonMethods<T> {
    set?<U extends T>(key: string, value: any): U;
    asMutable?(): T;
    without?<U>(key: string): U;
    without?<U>(keys: string[]): U;
    without?<U>(...args: string[]): U;
    without?<U>(keyFunction: (value: any, key: string) => boolean): U;
  }

  interface ImmutableArrayMethods<T> extends ImmutableCommonMethods<T> {
    set?<T>(index: number, value: any): Array<T>;
    asMutable?(): Array<T>;
    asObject?<U>(toKeyValue: (item: T) => Array<Array<any>>): U;
    flatMap?<U>(mapFunction: (item: T) => Array<U>)
  }

  type Immutable = {
    <T>(obj: Array<T>, options?: any): Array<T> & ImmutableArrayMethods<T>;
    <T>(obj: T, options?: any): T & ImmutableObjectMethods<T>;
    isImmutable(target: any): boolean;
    ImmutableError(message: string): Error;
  }

  var Immutable:Immutable;
  export = Immutable;

}

@Ciantic
Copy link

Ciantic commented Mar 22, 2016

@jonaskello I couldn't get the simple d.ts working when transpiling to es6, only thing working is in this format:

declare module "seamless-immutable" {
    export default function <T>(obj: T, options?: any): T;
    export function isImmutable(target: any): boolean;
    export function ImmutableError(message: string): Error;
    // ...
}

Btw, I've been thinking about something awesome, if JavaScript had API like this:

var frozenObject = Object.freeze({ something: [1,2,3,4,5] });

var mutatedFrozenObject = Object.mutate(frozenObject, (mutateObject) => {
    mutateObject.something[1] = 99;
    mutateObject.newValue = "test";
});

mutatedFrozenObject === {
    something: [1, 99, 3, 4, 5],
    newValue: "test"
};

The API would be probably simple enough for JavaScript engines to optimize the mutation function to work really fast, and it would allow to use all normal libraries to mutate frozen object.

Best of all, it would be 100% type safe way to mutate.

@zivni
Copy link

zivni commented Apr 4, 2016

I made a small change to what @jonaskello did to this:

declare module SeamlessImmutable {
    interface DeepMutate {
        deep: boolean
    }

    interface ImmutableCommonMethods<T>{
        setIn?<U extends T>(keys: Array<string|number>, value: any): T | U;
        merge?<U>(obj: U): T & U;
    }

    interface ImmutableObjectMethods<T> extends ImmutableCommonMethods<T> {
        set?<U extends T>(key: string, value: any): U;
        asMutable?(): T;
        asMutable?(DeepMutate): T;
        without?<U>(key: string): U;
        without?<U>(keys: string[]): U;
        without?<U>(...args: string[]): U;
        without?<U>(keyFunction: (value: any, key: string) => boolean): U;
    }

    interface ImmutableArrayMethods<T> extends ImmutableCommonMethods<T> {
        set?<T>(index: number, value: any): Array<T>;
        asMutable?(): Array<T>;
        asMutable?(DeepMutate): Array<T>;
        asObject?<U>(toKeyValue: (item: T) => Array<Array<any>>): U;
        flatMap?<U>(mapFunction: (item: T) => Array<U>)

    }
}

declare module 'seamless-immutable' {
  type Immutable = {
    <T>(obj: Array<T>, options?: any): Array<T> & SeamlessImmutable.ImmutableArrayMethods<T>;
    <T>(obj: T, options?: any): T & SeamlessImmutable.ImmutableObjectMethods<T>;
    isImmutable(target: any): boolean;
    ImmutableError(message: string): Error;
  }

  var Immutable:Immutable;
  export = Immutable;
}

This way I can import using import * as SI from "seamless-immutable"
and use it with state = SI<MyType>({...})
but I can also type my functions / variables:

let state: MyType & SeamlessImmutable.ImmutableObjectMethods<MyType >;
...
state = SI<MyType>({...})

and can create helper functions:

export function update<T>(obj:T & SeamlessImmutable.ImmutableObjectMethods<T>, update:any):T {
    return obj.merge(update);
}

export function removeProperty<T>(obj:T & SeamlessImmutable.ImmutableObjectMethods<T>, propertyName:string):T{
    return obj.without<T>(propertyName);
}

I only wish I could find a way to declare ImmutableObjectMethods<T> to include T's members so I wouldn't need to use the & operator everywhere

@pleimann
Copy link

pleimann commented May 17, 2016

@zivni You can declare a type which is the intersection of MyType and SeamlessImmutable.ImmutableObjectMethods so you won't need to use the '&' everywhere.

interface MyType {
    code: number;
    message: string;
};

type ImmutableMyType = MyType & SeamlessImmutable.ImmutableObjectMethods<MyType>;

let mt: ImmutableMyType = {
    code: 1000,
    message: 'It works!'
};

mt = mt.merge({message: 'It still works!'});

@dsebastien
Copy link

Did you guys make any progress on this one?

We've decided to use seamless-immutable @ work but we would like to have typings.
I'm willing to help work on the typings, so if you have any update, don't hesitate to share (gist?)

@jonaskello
Copy link
Author

Here are the latest typings we have, although we do not use them on our project yet:

declare module 'seamless-immutable' {
    interface AsMutableOptions {
        deep:boolean;
    }

    interface ImmutableCommonMethods<T> {
        setIn<U extends T>(keys:Array<string|number>, value:any):T | U;
        merge<U>(obj:U):T & U;
    }

    export interface ImmutableObjectMethods<T> extends ImmutableCommonMethods<T> {
        asMutable():T;
        asMutable(asMutableOptions:AsMutableOptions):T;
        set<U extends T>(key:string, value:any):U;
        setIn<U extends T>(key:Array<string>, value:any):U;
        update<U extends T>(key:string, updaterFunction:(value:any) => any):U;
        update<U extends T>(key:string, updaterFunction:(value:any, additionalParameter1:any) => any, arg1:any):U;
        update<U extends T>(key:string, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any) => any, arg1:any, arg2:any):U;
        update<U extends T>(key:string, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any) => any, arg1:any, arg2:any, arg3:any):U;
        update<U extends T>(key:string, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any, additionalParameter4:any) => any, arg1:any, arg2:any, arg3:any, arg4:any):U;
        updateIn<U extends T>(key:Array<string>, updaterFunction:(value:any) => any):U;
        updateIn<U extends T>(key:Array<string>, updaterFunction:(value:any, additionalParameter1:any) => any, arg1:any):U;
        updateIn<U extends T>(key:Array<string>, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any) => any, arg1:any, arg2:any):U;
        updateIn<U extends T>(key:Array<string>, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any) => any, arg1:any, arg2:any, arg3:any):U;
        updateIn<U extends T>(key:Array<string>, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any, additionalParameter4:any) => any, arg1:any, arg2:any, arg3:any):U;
        without<U>(key:string):U;
        without<U>(keys:string[]):U;
        without<U>(...args:string[]):U;
        without<U>(keyFunction:(value:any, key:string) => boolean):U;
    }

    export interface ImmutableArrayMethods<T> extends ImmutableCommonMethods<T>, Array<T> {
        set<T>(index:number, value:any):Array<T>;
        asMutable():Array<T>;
        asMutable(asMutableOptions:AsMutableOptions):Array<T>;
        asObject<U>(toKeyValue:(item:T) => Array<Array<any>>):U;
        flatMap<U>(mapFunction:(item:T) => Array<U>):U;
    }

    export function from<T>(obj: Array<T>, options?: any): Array<T> & ImmutableArrayMethods<T>;
    export function from<T>(obj: T, options?: any): T & ImmutableObjectMethods<T>;

    export function isImmutable(target: any): boolean;
    export function ImmutableError(message: string): Error;
}

If I recall correctly we had trouble with the default exported function so we skipped doing typings for that and instead went with using the from() method as it is a named export which is easier to do typings for. So usage would be something like:

import * as SI from 'seamless-immutable'

const myImmutable = SI.from(myObj);

As a side note I am also looking into alternate ways of achieving immutability in typescript. I have found two ways so far. First you can in ts 2.0 (or at least in 1.9 pre-release) declare your interfaces as readonly like this:

interface Foo {
    readonly x:string,
    readonly y:number
}

Second you can try to achieve immutability by tslint rules. I am doing some work on that here.

@zivni
Copy link

zivni commented Jul 7, 2016

Thanks for the update.
I like to keep the typing (interfaces and so) in a separate module. that way I can just reference the directly without import.
With type Immutable = { <T>(obj: Array<T>, options?: any)... as I posted above I can use SI(myObj) without the from function.

Here is my typing after I've included missing typing that @jonaskello added:

declare module SeamlessImmutable {
    interface DeepMutate {
        deep: boolean
    }
    interface ImmutableCommonMethods<T> {
        setIn?<U extends T>(keys: Array<string | number>, value: any): T | U;
        merge?<U>(obj: U, options?: DeepMutate): T & U;
    }

    interface ImmutableObjectMethods<T> extends ImmutableCommonMethods<T> {
        set?<U extends T>(key: string, value: any): U;
        asMutable?(): T;
        asMutable?(AsMutableOptions): T;
        without?<U>(key: string): U;
        without?<U>(keys: string[]): U;
        without?<U>(...args: string[]): U;
        without?<U>(keyFunction: (value: any, key: string) => boolean): U;
    }

    interface ImmutableArrayMethods<T> extends ImmutableCommonMethods<T>, Array<T> {
        set?<T>(index: number, value: any): Array<T>;
        asMutable?(): Array<T>;
        asMutable?(DeepMutate): Array<T>;
        asObject?<U>(toKeyValue: (item: T) => Array<Array<any>>): U;
        flatMap?<U>(mapFunction: (item: T) => Array<U>)
    }
}

declare module 'seamless-immutable' {
    type Immutable = {
        <T>(obj: Array<T>, options?: any): SeamlessImmutable.ImmutableArrayMethods<T>;
        <T>(obj: T, options?: any): T & SeamlessImmutable.ImmutableObjectMethods<T>;
        isImmutable(target: any): boolean;
        ImmutableError(message: string): Error;
        from<T>(obj: Array<T>, options?: any): SeamlessImmutable.ImmutableArrayMethods<T>;
        from<T>(obj: T, options?: any): T & SeamlessImmutable.ImmutableObjectMethods<T>;
    }

    var Immutable: Immutable;
    export = Immutable;
}

@dsebastien
Copy link

Thanks a lot @jonaskello. I spent all morning battling against those typings and couldn't find a way to re-export a type corresponding to the Immutable object returned by seamless-immutable... just like you said.

Your comment helped me move forward.

Here's what I've ended up with:

declare module "seamless-immutable" {
                interface ImmutableCommonMethods<T>{
                               setIn?(propertyPath: Array<string|number>, value: any): Immutable<T>;
                               merge?(part: T): Immutable<T>;
                }

                export interface ImmutableObject<T> extends ImmutableCommonMethods<T> {
                               set(property: string, value: any): Immutable<T>;
                               setIn(propertyPath:Array<string>, value:any):Immutable<T>;

                               asMutable(): T;
                               asMutable(opts:AsMutableOptions): T;

                               update(property:string, updaterFunction:(value:any) => any):Immutable<T>;
                               update(property:string, updaterFunction:(value:any, additionalParameter1:any) => any, arg1:any):Immutable<T>;
                               update(property:string, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any) => any, arg1:any, arg2:any):Immutable<T>;
                               update(property:string, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any) => any, arg1:any, arg2:any, arg3:any):Immutable<T>;
                               update(property:string, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any, additionalParameter4:any) => any, arg1:any, arg2:any, arg3:any, arg4:any):Immutable<T>;

                               updateIn(propertyPath:Array<string>, updaterFunction:(value:any) => any):Immutable<T>;
                               updateIn(propertyPath:Array<string>, updaterFunction:(value:any, additionalParameter1:any) => any, arg1:any):Immutable<T>;
                               updateIn(propertyPath:Array<string>, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any) => any, arg1:any, arg2:any):Immutable<T>;
                               updateIn(propertyPath:Array<string>, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any) => any, arg1:any, arg2:any, arg3:any):Immutable<T>;
                               updateIn(propertyPath:Array<string>, updaterFunction:(value:any, additionalParameter1:any, additionalParameter2:any, additionalParameter3:any, additionalParameter4:any) => any, arg1:any, arg2:any, arg3:any):Immutable<T>;

                               without(property:string):Immutable<T>;
                               without(propertyPath:string[]):Immutable<T>;
                               without(...properties:string[]):Immutable<T>;
                               without(filter:(value:any, key:string) => boolean):Immutable<T>;
                }

                export interface ImmutableArray<T> extends ImmutableCommonMethods<T> {
                               set(index: number, value: any): ImmutableArray<T>;
                               asMutable(): Array<T>;
                               asMutable(opts:AsMutableOptions): Array<T>;
                               asObject(toKeyValue: (item: T) => Array<Array<any>>): ImmutableArray<T>;
                               flatMap(mapFunction: (item: T) => ImmutableArray<T>): any;

                               // TODO review methods (missing ones for arrays?)
                }

                interface Options {
                               prototype?: any;
                }

                interface AsMutableOptions {
                               deep: boolean;
                }

                // an immutable object is both of Type T (i.e., looks like a normal T) and of type Immutable<T>
                export type Immutable<T> = T & (ImmutableObject<T> | ImmutableArray<T>);

                // TODO it would be ideal to be able to expose that type and have the variable available from client code
                // couldn't figure out how to do this unfortunately
                /*
                export type SeamlessImmutable = {
                               <T>(obj: T, options?: Options): T & ImmutableObject<T>;
                               <T>(obj: Array<T>, options?: Options): Array<T> & ImmutableArray<T>;
                               from:SeamlessImmutable;
                               isImmutable(target: any): boolean;
                               ImmutableError(message: string): Error;
                };

                export const Immutable: SeamlessImmutable;
                */

                export function from<T>(obj: T, options?: Options): T & ImmutableObject<T>;
                export function from<T>(obj: Array<T>, options?: Options): Array<T> & ImmutableArray<T>;

                export function isImmutable(target: any): boolean;
                export function ImmutableError(message: string): Error;
}

I'll try to take some time and publish those typings as an npm package.
I also made a "global" version for cases where seamless-immutable is loaded as a script tag (i.e., when it attaches Immutable to the window object).

@dsebastien
Copy link

The advantage of the above is that in my code I can import the same way you mentionned, but also import the Immutable type separately, which makes for a nicer TS API:

import * as SeamlessImmutable from "seamless-immutable";
import {Immutable} from "seamless-immutable";
...
const immutablePerson:Immutable<Person> = SeamlessImmutable.from(new Person());
...

@jonaskello
Copy link
Author

@zivni You may already be aware of this but if you want your typings global like that I would recommend using the namespace syntax instead. The module without quotes syntax is not preferred anymore (since ts 1.5). So instead of declare module SeamlessImmutable { ...} I would recommend declare namespace SeamlessImmutable { ...}. They are totally equivalent so technically it does not matter. You can read more in the official ts documentation:

A note about terminology: It’s important to note that in TypeScript 1.5, the nomenclature has changed. “Internal modules” are now “namespaces”. “External modules” are now simply “modules”, as to align with ECMAScript 2015’s terminology, (namely that module X { is equivalent to the now-preferred namespace X {).

@MarkusKramer
Copy link

Nice work. @dsebastien could you add your file to tsd/npm so that anyone can use it easily?

@mnasyrov
Copy link

It may be better to include the typings into seamless-immutable package itself (https://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html), thus no external tsd/npm package will be required.

@dsebastien
Copy link

Actually I think typings should be in their own package. That's the way the TypeScript team pushes forward now with TS 2.0+ (see here: https://blogs.msdn.microsoft.com/typescript/2016/06/15/the-future-of-declaration-files/)

The reason is that people don't necessarily want to release a new version of a library because the typings are incomplete or buggy. I guess there are pros and cons to both approaches

@jonaskello
Copy link
Author

@dsebastien I was under the impression that the way moving forward is that the typings author should make a PR to the DefinitelyTyped repo, not produce his own npm package. Microsoft will then export DefinitelyTyped to @types npm packages automatically. Could you quote where it says they should be put in their own package?

@mnasyrov
Copy link

@dsebastien I think it is good when something forces authors to keep complete and reliable typings).

@dsebastien
Copy link

@jonaskello might be the latest news, I don't know ^^
@mnasyrov well I believe in TypeScript so I see its value, but developers of pure JS libs might simply not care enough ^^

@cvle
Copy link

cvle commented Sep 28, 2016

I'm experimenting with the types from @dsebastien.

I added this definition:

export function from<T, U>(obj: Array<T>, options?: Options): Array<T & U> & ImmutableArray<T>;

This lets me do the following:

const articleList = SeamlessImmutable.from<Article, ImmutableObject<Article>>(new Array<Article>(...articles));
articleList[0].set("title", "anotherTitle");

Which is quite handy, otherwise I would need to do some ugly type casting to get the correct type for the elements in the array.

Maybe the types in ImmutableArray can be more strict like in set(index: number, value: any): ImmutableArray<T>;, my thought was it should be set(index: number, value: T): ImmutableArray<T>;, but I'm a relative new user of this library, so I'm not really sure.

It'd be awesome if someone has the time to work on a PR on https://github.com/DefinitelyTyped/DefinitelyTyped.

@alex3165
Copy link

alex3165 commented Oct 4, 2016

I have opened this PR DefinitelyTyped/DefinitelyTyped#11717 from @dsebastien work, I did some questionable choices and I am happy to reply to any questions. I hope it will help :)

@alex3165
Copy link

alex3165 commented Oct 6, 2016

For information the PR has been merged, feel free to open a new PR on DefinitelyTyped to improve the type definition file in case there are some quirks.

@rtfeldman
Copy link
Owner

Awesome! If @alex3165 if you'd like to open a PR to seamless-immutable to add a link to the typings in our README, I would be happy to accept it.

Thanks for all the hard work on this!

@cubabit
Copy link

cubabit commented Oct 21, 2016

How are you supposed to use the type definition in https://github.com/alex3165/DefinitelyTyped/blob/14740e2ee4239622065cbc674d793586f5cf24b7/seamless-immutable/seamless-immutable.d.ts?

I have tried these:

import Immutable from "seamless-immutable";
import {Immutable} from "seamless-immutable";
import * as Immutable from "seamless-immutable";

but neither give me an Immutable function I can use:

const state = Immutable({});

TypeScript says error TS2349: Cannot invoke an expression whose type lacks a call signature.

@alex3165
Copy link

If you use typings to manage your type declaration file :

typings install dt~seamless-immutable --save --global

or now with typescript 2 you can use npm to manage the types :

npm install --save-dev @types/seamless-immutable

Then you have to import seamless-immutable like this :

import * as SI from 'seamless-immutable';

@cubabit
Copy link

cubabit commented Oct 21, 2016

Thanks, but now If I try and do:

const initialState = SI.Immutable({});

I get:

src/reducers/bookings/reducer.ts(10,22): error TS2349: Cannot invoke an expression whose type lacks a call signature.
src/reducers/calendar/reducer.ts(10,25): error TS2339: Property 'Immutable' does not exist on type 'typeof SeamlessImmutable'.

I thought, looking at the code at https://github.com/rtfeldman/seamless-immutable there should be an Immutable function available?

@alex3165
Copy link

Try:

// for an object
SI.from({});
// for an array
SI.from([])

@cubabit
Copy link

cubabit commented Oct 21, 2016

OK great - Thank you! At least I can get moving now! But why does using the typescript definition mean you can't used the documented interface?

@alex3165
Copy link

I have just ported the last type definition file from above to the definitelyTyped registry and cleaned it a bit so I am not too sure about this from choice. It is something that need to be fixed or documented indeed.

@draunkin
Copy link

draunkin commented Nov 9, 2016

@alex3165 I had just installed the definitions from npm install @types/seamless-immutable and I get a different file than that hosted on DefinitelyTyped. It seems the one installed using @types... is out of date as I could not get it to work properly with an error stating the from function was not available. The new definition you have at DefinitelyTyped works because it declares the 'seamless-immutable' module correctly.
Took me a while to figure out what was going on, not sure the process but maybe you have to update the one installed by @types...

Hope this helps anyone else struggling with it. Cheers

@jonaskello
Copy link
Author

Sine typescript 2.0 It is now possible to achieve immutability at compile-time by just using the readonly keyword and ReadonlyArray. That is what I have been doing lately. I also made tslint-immutable which uses tslint rules to make sure that readonly is enforced.

@MichaelTontchev
Copy link

@jonaskello readonly is great, but the problem is that it can't be made recursive yet, pending microsoft/TypeScript#10725

As to your library, I haven't used it yet, but it looks nice. I just wish that you could limit the linting rules to certain files: jonaskello/tslint-immutable#25

@babakness
Copy link

Commenting to follow this conversation.

@toranb
Copy link

toranb commented Jul 16, 2017

I ended up writing a new type definition file for seamless-immutable 7 that works well with TypeScript 2.4

I've had success writing product types like you see below

import { ImmutableObject } from 'seamless-immutable';

type ImmutableState<T> = ImmutableObject<T> & T;

export default ((state: ImmutableState<UserState>, action: Action): ImmutableState<UserState> => {
  switch(action.type) {
    case 'ASSIGN_USER': {
      return state.merge({number: action.number});
    }
    default: {
      return state || initialState;
    }
  }
});

For anyone interested, I threw together a full example app with redux/typescript 2.4/seamless 7.1.2

@Connormiha
Copy link

@toranb What about Immutable.static?

@toranb
Copy link

toranb commented Sep 27, 2017

@Connormiha I'm down to add something for it - my hacking was mostly for methods I was using (if this is something you are interested in providing type defs for I'd be willing to update it). Might ping me on that repo instead to start a thread about it

@lifehackett
Copy link

@toranb @Connormiha where did y'all end up with the static typings?

@toranb
Copy link

toranb commented May 10, 2018

@lifehackett I've got a privately maintained typedef for this lib that works w/ typescript 2.7

https://github.com/toranb/seamless-immutable-tsdefs/commits/master

note: I'm not actively using much typescript these days but I did manage to throw together a simple reducer using these^ typedefs and seamless immutable a while back

https://github.com/toranb/ember-redux-yelp/blob/branches/seamlessAndTypeScript/app/reducers/users.ts

@lifehackett
Copy link

Thanks for the response @toranb . I don't see a fix for the static typings in your typedef repo though. The typedefs in this lib and your repo work fine for something like state.set('foo', 'bar'), but fall apart when you use the static syntax (which is recommended by the seamless-immutable authors) like Immutable.set(state, 'foo', 'bar').

I am able to extend it locally like this

declare module 'seamless-immutable' {
  export function set<K extends keyof T>(obj: ImmutableObject<T>, property: K, value: T[K]): ImmutableObject<T>;
  export function set<TValue>(obj: ImmutableObject<T>, property: string, value: TValue): ImmutableObject<T>;
}

Not sure if that is exactly the correct signature as this is my first foray into TS, but it does remove the TS errors. I'll look to contribute these back to the repo when I have some time to flush them all out, but thought I'd check that it hadn't already been done first. Thanks again

@toranb
Copy link

toranb commented May 10, 2018

@lifehackett fair point! I've also not tried this yet w/ typescript v2.8+

I'm happy to add you as a contributor to that one off typedefs repo if you get deep into it ;)

@Fire7
Copy link

Fire7 commented May 24, 2018

Why do getIn return type Immutable<T> , not ImmutableObject<T> ?

interface IInterface {
  str: string;
}

type TImmutableType = ImmutableObject<IInterface>;

const immutableObject: TImmutableType = Immutable({
  str: 'simple string' 
});

const getString = (): string => immutableObject.getIn(['str']).asMutable();
                      ^^^^^^^
TS2322: Type 'string | string[]' is not assignable to type 'string'. 
  Type 'string[]' is not assignable to type 'string'

Is it a bag? Do we need to duplicate typings of getIn in ImmutableArray ? Or how I can fix it?

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

No branches or pull requests