Skip to content

Record inheritance in Typescript? #166

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
abergs opened this issue Oct 30, 2014 · 22 comments
Closed

Record inheritance in Typescript? #166

abergs opened this issue Oct 30, 2014 · 22 comments
Milestone

Comments

@abergs
Copy link

abergs commented Oct 30, 2014

I were struggling with converting a model of ours to a Immutable.Map and found Record, which is perfect because we need to implement some functions.

But It doesn't seem to be possible to use the example syntax i Typescript?

import Immutable = require("immutable");

class ABRecord extends Immutable.Record({ a: 1, b: 2 }) {
    getAB() {
        return this.a + this.b;
    }
}

var myRecord = new ABRecord(b:3);
myRecord.getAB();// 4

Doesn't compile at all, "The property 'Record' does not exist on value of type 'Immutable'."
Any tips?

@leebyron
Copy link
Collaborator

Hmm.. I'll look into this for you. What you've typed up there seems very reasonable, but sometimes TypeScript requires some convincing.

@abergs
Copy link
Author

abergs commented Oct 31, 2014

Are you able to compile the code above? I'm running TS 1.0.3.0. Wondering if TS requires a an actual class to allow extending. The documentation however says extends classType: 8.1.1 http://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf

@leebyron
Copy link
Collaborator

leebyron commented Nov 1, 2014

Ah yes, that example is valid ES6, but not TypeScript.

I haven't tried this, but perhaps this might work:

var _ABRecord = Immutable.Record({ a: 1, b: 2 });
class ABRecord extends _ABRecord {
    getAB() {
        return this.a + this.b;
    }
}

If not, I'll have to look into how to get this working in TypeScript in better form.

@abergs
Copy link
Author

abergs commented Nov 3, 2014

Unfortunately not; TypesScript is struggling a lot with this.
ts01

@leebyron leebyron added the 4.0 label Jun 18, 2015
@leebyron leebyron modified the milestone: 4.0 Jun 18, 2015
@leebyron leebyron removed the 4.0 label Jun 18, 2015
@Keats
Copy link

Keats commented Jul 27, 2015

Hey @leebyron I see you have added a 4.0 label on that issue.
Any indication on how the records are going to be changed in regards to typescript (also regarding #341)

@leebyron
Copy link
Collaborator

Hey! I'm not sure yet actually and am open to suggestions.

@Keats
Copy link

Keats commented Jul 29, 2015

I'll play with it hopefully around next week and will report

@Keats
Copy link

Keats commented Aug 3, 2015

After looking around a bit, I think adding generics to some of the types like Record would solve at least the typing issues in #341 and #564 .

As for inheriting, I'm not entirely sure how that'd work since microsoft/TypeScript#2225 doesn't seem to be worked on. So maybe inheritance for a future version and just make it generic for now?

Let me know if you need to add generics tests for the next version

@endash
Copy link

endash commented Sep 11, 2015

Is this related to the present inability to type alias, e.g. type MyRecord = Record({foo: ""})? And, consequently, the inability to use a Record as a type in other ways: List<MyRecord>. Assigning to a var and attempting the latter scenario results in a similar error message: Cannot find name 'MyRecord'.

@aindlq
Copy link

aindlq commented Oct 24, 2015

I wonder if it is possible to do this with https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#extending-expressions, microsoft/TypeScript#4910 and intersection types (see #341 (comment)). All of the above is available in nightly ts build.

Any ideas?

@yoshiso
Copy link

yoshiso commented Oct 28, 2015

Works fine like below, with Typescript 1.6.

import Immutable = require("immutable");

const defaultValue = { a: 1, b: 2 }

class ABRecord extends Immutable.Record(defaultValue) {
    getAB() {
        return this.a + this.b;
    }
}

var myRecord = new ABRecord(b:3);
myRecord.getAB();// 4

@endash
Copy link

endash commented Oct 28, 2015

The subclassing works now (1.6) but the compiler will put off errors about properties not existing if you don't explicitly declare them:

const defaultValue = { a: 1, b: 2 }

class ABRecord extends Immutable.Record(defaultValue) {
    a: number;
    b: number;

    getAB() {
        return this.a + this.b;
    }
}

@endash
Copy link

endash commented Feb 12, 2016

Back to not working with 1.7. Argh.

@zhenwenc
Copy link

I spent a long time trying to the same issue, but at the end of the day, I can't answer the most simple question (maybe someone here can help): Why bother using Immutable.Record in Typescript?

  • does a class with no setter method can be considered as immutable?
  • should we only use ImmutableJS for collections?

@endash
Copy link

endash commented Feb 26, 2016

@zhenwenc Such a class would be immutable, but would not have the set functionality, nor default values support. That said, I'm personally inclined towards custom native-TS classes, considering how much hassle it is trying to use Immutable.Record—especially if a solution is liable to break with future versions of TS.

@gergelyhegykozi
Copy link

This "workaround" works for me (functionality and code completion wise without errors):

import { Record, Map } from 'immutable';

export interface IModel {
    a?: number;
    b?: number;
}

interface IModelBase extends IModel, Map<string, any> {}

interface ModelBase extends IModel {
    new (values: {[key: string]: any}): IModelBase;
    getAB?: () => number;
}

export type Model = Record.IRecord<ModelBase>;

const ModelBaseRecord: ModelBase = Record({
    a: 1,
    b: 2,
});

class ModelRecord extends ModelBaseRecord {
    public getAB = () => this.a + this.b;
}

export default ModelRecord;

@danielfigueiredo
Copy link

danielfigueiredo commented Jul 26, 2016

@leonuh @abergs I've created this library that has an interface called TypedRecord that extends from Immutable.Map and changes all return types to the generic provided type.
It has a function called makeTypedFactory that will create Immutable.Record of the type you specify, it's a just a utility but allows you to have typed Record all over the place and is a very very small library.
https://github.com/rangle/typed-immutable-record

@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

@leebyron
Copy link
Collaborator

leebyron commented Mar 8, 2017

Latest type definitions are helping out quite a bit more, will be released soon

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

I created simple typescript lib to create immutable.js record classes with full inheritance support - https://github.com/MrDegriz/immutable-record-class. Maybe it helps to someone.

@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: []
    });

@paramsingh88
Copy link

Why is this closed. The problem persists.

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

14 participants