34

I have the following JSON object in my Angular 2 app and would like to know what is the proper what to declare it in typescript.

data = [
  {
    'id':1,
    'title':'something'
    'node': [
              {
              'id':1,
              'title':'something'
              'node': []
              }
            ]
  },
  {
    'id':2,
    'title':'something'
    'node': [
              {
              'id':1,
              'title':'something'
              'node': []
              }
            ]
  }
]
3
  • What do mean by proper way? How to import int TS file? Commented Jun 30, 2016 at 12:37
  • you want to declare a recursive component? Commented Jun 30, 2016 at 12:38
  • It would be better for me to say this is a Javascript object. Commented Jun 30, 2016 at 13:05

5 Answers 5

29

Here is an easy and naive implementation of what you're asking for:

interface IDataNode {
    id: number;
    title: string;
    node: Array<IDataNode>;
}

If you want to instantiate said nodes from code:

class DataNode implements IDataNode {
    id: number;
    title: string;
    node: Array<IDataNode>;

    constructor(id: number, title: string, node?: Array<IDataNode>) {
        this.id = id;
        this.title = title;
        this.node = node || [];
    }

    addNode(node: IDataNode): void {
        this.node.push(node);
    }
}

Using this to hardcode your structure:

let data: Array<IDataNode> = [ 
    new DataNode(1, 'something', [
        new DataNode(2, 'something inner'),
        new DataNode(3, 'something more')
    ]),
    new DataNode(4, 'sibling 1'),
    new DataNode(5, 'sibling 2', [
        new DataNode(6, 'child'),
        new DataNode(7, 'another child', [
            new DataNode(8, 'even deeper nested')
        ])
    ])
];
Sign up to request clarification or add additional context in comments.

7 Comments

you might want to make the node property in the interface an array as well. :)
Hello @rinukkusu if the data came from json, you didn't even need to declare a class, you can parse the json and define a contract with the interface, thats the beauty of typescript.
Yes, that's right. That's why I said "instantiate"! :)
I like this answer. But, lets say I just want to hard code to data in my component. I understand this is not the right way to do things. I'm just curious.
@rinukkusu, Yes sorry, I didn't see that part.
|
24

Update: 7/26/2021

I revisited the discussion noted in the original answer, and there is now an update with an improved implementation.

type JSONValue = 
 | string
 | number
 | boolean
 | null
 | JSONValue[]
 | {[key: string]: JSONValue}

interface JSONObject {
  [k: string]: JSONValue
}
interface JSONArray extends Array<JSONValue> {}

This has been working very well for me.

Discussion reference: https://github.com/microsoft/TypeScript/issues/1897#issuecomment-822032151

Original answer: Sep 29 '20

I realize this is an old question, but I just found a solution that worked very well for me. Declare the following

type JsonPrimitive = string | number | boolean | null
interface JsonMap extends Record<string, JsonPrimitive | JsonArray | JsonMap> {}
interface JsonArray extends Array<JsonPrimitive | JsonArray | JsonMap> {}
type Json = JsonPrimitive | JsonMap | JsonArray

then any of the following (including the OP's version slightly modified for syntax errors) will work

    let a: Json = {};
    
    a[1] = 5;
    a["abc"] = "abc";
    a = {
      a: {
        a: 2,
      },
      b: [1, 2, 3],
      c: true,
    };
    a = [
      {
        "id": 1,
        "title": "something",
        "node": [
          {
            "id": 1,
            "title": "something",
            "node": [],
          },
        ],
      },
      {
        "id": 2,
        "title": "something",
        "node": [
          {
            "id": 1,
            "title": "something",
            "node": [],
          },
        ],
      },
    ];

This answer should be credited to Andrew Kaiser who made the suggestion on the discussion about making Json a basic type in Typescript: https://github.com/microsoft/TypeScript/issues/1897#issuecomment-648484759

1 Comment

I didn't try your solution because some people have stated problems with it, but +1 for linking to the discussion.
6

The proper way is using an interface, it doesn't generate extra code when compiled to javascript and it offers you static typing capabilities:

https://www.typescriptlang.org/docs/handbook/interfaces.html

Comments

1
type JsonValue = string | number | boolean | null;
type JsonArray = Array<JsonValue | JsonObject>;
interface JsonObject {
    [key: string]: JsonValue | JsonObject | JsonArray;
}

2 Comments

Add Json type as union of all types you've defined. Json is not just an object, but array or primitive too.
@WojciechTomczyk you are right, according to the JSON RFC 7159 (datatracker.ietf.org/doc/html/rfc7159) the arrays and primitives are proper jsons
0

This kind of types are so generic that they should belong to an external tested lib.

So most widespread, for what I know, having these types is type-fest

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.