5

I have a tree structure that looks something like this:

interface TreeData {
  id : number;
  text : string;
  children : TreeData[];
}

I want to wrap this up into an Immutable Map but since I'm using TypeScript, I would like some type safety around using get and set. I found 2 articles online for suggestions on how one might be able to do this, see:

https://github.com/facebook/immutable-js/issues/683 https://blog.mayflower.de/6630-typescript-redux-immutablejs.html

However my problem is that my TreeData contains a list of TreeData children. If I define my ImmutableMap this way:

export interface ImmutableMap<T, K, V> extends Map<K, V> {
  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>;
}

Then when I try to define the following, I get a typescript error message:

type ImmutableTreeData = ImmutableMap<TreeData, string, string | List<ImmutableTreeData>>;

[TS] Type alias ImmutableTreeData circularly references itself

How can I fix this TypeScript error?

Marek Krzeminski
  • 1,308
  • 3
  • 15
  • 40
  • Isn't having a `set` operation on a class called `ImmutableMap` counterintuitive? – Ian MacDonald Oct 15 '18 at 20:30
  • 1
    ImmutableJs's Map.set function returns a new Map. See https://facebook.github.io/immutable-js/docs/#/Map/set – Marek Krzeminski Oct 15 '18 at 20:36
  • Sorry, I didn't mean to indicate that I thought you were wrong. I just think the naming is strange. – Ian MacDonald Oct 15 '18 at 20:45
  • 1
    https://stackoverflow.com/questions/42352858/type-alias-circularly-references-itself#42354422 – Nathan Bierema Oct 15 '18 at 20:46
  • 1
    Does this answer your question? [TypeScript | Immutable | proper way of extending Immutable.Map type](https://stackoverflow.com/questions/43607652/typescript-immutable-proper-way-of-extending-immutable-map-type) – chantey Jul 17 '22 at 23:43

2 Answers2

2
import { Map } from "immutable";

export interface ITypedMap<T> extends Map<string, any> {
  get<K extends keyof T>(name: K): T[K];
}

export function TypedMap<T>(value: T): ITypedMap<T> {
  return Map(value) as ITypedMap<T>;
}

Usage:

const myMap = TypedMap(value);

References:

gpresland
  • 1,690
  • 2
  • 20
  • 31
  • I used this for a small simple case, but it does not work for any function besides `.get`. There must be a better solution somewhere out there, but I cannot find it. – Dawson B Jan 10 '20 at 20:42
0

How about a record?

interface ITreeData {
  id: number;
  text: string;
  chilren?: ITreeData;
}

const defaultTreeData: ITreeData = {
  id: 0,
  text: "",
  chilren: undefined
};

class TreeData extends Record(defaultTreeData) {
  public static Init() {
    return new TreeData(defaultTreeData);
  }
}

const treeData = TreeData.Init().set("text", "ok");

const treeData2 = treeData.set("chilren", treeData);

console.log(treeData2);
alp ates
  • 81
  • 1
  • 4
  • I didn't want to use records because of the reasons mentioned here: https://blog.mayflower.de/6630-typescript-redux-immutablejs.html – Marek Krzeminski Oct 16 '18 at 15:12
  • I see your point. They really weaken the type safety. Note: New typings are now allowing of use `recordInstance.user`. if you need them. – alp ates Oct 18 '18 at 07:02