PhiceDev PhiceDev - 1 month ago 9
TypeScript Question

Typescript , what's the best approach for declaring a nested object with different types?

I have this JS object and I want to port it to Typescript:

var items = [{
style: 'activity-8',
color: '#a32c62',
id: 8,
done : false,
label : {
short :'999 m',
long :'walk 999m',
statstime :'yesterday'
},
val : {
today : {
target : {
raw : 0,
display :"11"
},
achieved : {
raw : 0,
display :"22"
}
},
yesterday : {
target : {
raw : 0,
display :"33"
},
achieved : {
raw : 0,
display :"44"
}
}
}
},{
style: 'activity-7',
color: '#ec575d',
id: 7,
done : true,
label : {
short :'walk 555m',
long :'walk 555m',
statstime :'yesterday'
},
val : {
today : {
target : {
raw : 0,
display :"0"
},
achieved : {
raw : 0,
display :"0"
}
},
yesterday : {
target : {
raw : 0,
display :"0"
},
achieved : {
raw : 0,
display :"0"
}
}
}
}];


what's the best approach to declare this object type? shall I write down the types for every field? Or create a custom type? any other suggestions?

Answer

I don't know if it's the "best" way but I will give you example of my way of doing this.

I will try to explain my general rule not only for nested objects:

For all the properties that are base type (string, number, boolean or some Array of something) you can leave them like this, but for every other complex property/nested object that from now on I will call 'Complex property' (because it makes more sense to me) you make an Interface that will be the type of the property.

Example: In your case the val property is a 'complex' property so let's split it, starting bottom to top.

The smallest complex property in the val property is target, so you make an interface called Target (or ITarget it's not exactly a convention to do that in ts) Target will be something like:

interface Target {
  raw: number,
  display: string
}

You do the same thing for the achieved 'complex' property.

Now you can go one level up. Today property is also a 'complex' one so it has to have a type that probably is going to be some type of interface. Thanks to our previous work that interface will look like:

interface Day {
  target: Target,
  achieved: Achieved
}

You are probably wondering why the interface is called Day and not Today, well the reason is that you have to find out what is the type of the yesterday 'complex' property. As you can see you can see it is the same type as the today property because it has the same properties inside.

So finally the val property that was our goal will have it's own interface that will be something like:

interface Val {
  today: Day,
  yesterday: Day
}

Next you do the same thing for every other 'complex' property.

Now you can use those in your main object which probably will be a class and its properties' types will be those interfaces.

Don't forget that the interface in ts(and not only) is just a 'helping' tool that only helps to organize your code design. Every interface most of the time should has to have a class that implements that interface. And those are the classes that you will use to actually set some values to those properties.