Nesting TypeScript interfaces can be a great way of keeping your code neat and clean. To clarify the term ‘nesting’, I simply mean that interface A contains a property, which is described by interface B.
If you’re curious what types can be used in an interface, you can checkout my article on an Overview of TypeScript Types.
Interface Recap
Before we get into nesting, let me just remind those who aren’t sure how an interface looks like.
declare interface ICar {
licensePlate: string;
colour: string;
dateOfRegistration: Date;
}
// using the interface
const newCar: ICar = {
licensePlate: 'KJ09 VTP',
colour: 'Sunshine Yellow',
dateOfRegistration: Date.now()
}
Nesting the Interface
So lets continue the car theme, and assume we want the car interface to have a property that holds the type of tyres fitted.
To begin with, lets make the assumption that in order to describe a tyre, we need it’s width, type profile and diameter.
Note: you might find this on your car read like 215/60R15, which reads 215mm wide, 60 mm profile and 15 inches in diameter.n
declare interface ITyre {
width: number;
profile: number;
diameter: number;
}
const newType: ITyre = {
width: 215,
profile: 60,
diameter: 15
}
Moving on. To make a wheel, part of the car (nesting the interfaces). We need only to describe a property with in the interface by another interface as a complex type.
declare interface ITyre {
width: number;
profile: number;
diameter: number;
}
declare interface ICar {
licensePlate: string;
colour: string;
dateOfRegistration: Date;
tyreType: ITyre;
}
// using the new nested interface
const newCar: ICar = {
licensePlate: 'KJ09 VTP',
colour: 'Sunshine Yellow',
dateOfRegistration: Date.now(),
tyreType: {
width: 215;
profile: 60;
diameter: 15;
}
}
Why Do We Do It?
Shouldn’t we just describe a tyre as a built in part of a car than it’s own interface?
In trying to adhere to the DRY methodology, treating the tyre as a part of a car implies that a tyre is not a complete object in it’s own right (object being the case of something that is a noun here).
What do I mean by this? It is entirely probably that you may need to treat a tyre as a separate entity from the car. You may purchase a tyre, but not a car. In order to program this you would have to have two objects that describe exactly the same thing.
This is of course a bad thing. If you are asked to add a new property to a tyre, say a true / false for low profile tyres, then you might make the change in one place but not the other. For example:
original car interface without the nested tyre interface
declare interface ICar {
licensePlate: string;
colour: string;
dateOfRegistration: Date;
tyreType: {
width: number;
profile: number;
diameter: number;
};
}
// newly changed tyre interface
declare interface ITyre {
width: number;
profile: number;
diameter: number;
lowProfile: boolean;
}
Now when selling a tyre, you will be able to utilise the new low profile property. However when interacting with a car the property won’t exist. By using the ITyre interface as a property of the ICar interface, you can ensure that any changes are persisted through your code. Long and the short, don’t repeat yourself!
Nesting Interfaces in Arrays
Most people know that a car has a tyre size. But did you know that your car may have two different sizes? Front and back tyres often are different due to tread pattern and varying other factors. For this reason, we may want to specify all the tyres within our car interface.
To do this without duplicating the ‘tyreType: ITyre’ three additional times, we should store this in an array.
declare interface ITyre {
width: number;
profile: number;
diameter: number;
lowProfile: boolean;
}
declare interface ICar {
licensePlate: string;
colour: string;
dateOfRegistration: Date;
tyres: ITyre[];
///// or /////
tyres: Array<ITyre>;
}
In general practice guidelines from TypeScript documentation; we should use the first example of the typed array over the second due to it being a type reference, mostly because of readability.
There has been questions over this on stack overflow, however the generally accepted answer can be found here.