- Read Tutorial
- Watch Guide Video
In the last guide, we walked through how to create a class in typescript is pretty basic from a syntax point of view. We created a class with a constructor and then we call it instantiated the class a few times so that we could print things out and combine a little bit of behavior at data. But now we're going to get into one of the more important concepts of object-oriented programming a class by itself is kind of pointless. There's not really any reason why you would ever use a class to design a program classes’ strength comes from being able to use the concept of inheritance which means that you have the ability to create abstract classes and classes that have high-level functionality and that kind of thing. And you can then implement inherited classes that make essentially specialized versions of that class and that's what we're going to do in this guide.
So I'm going to get rid of all of this code here and instead of having an invoice where eventually go create an invoice but I'm going to start off with an abstract class or a class that is going to be called Report. It's not technically an abstract class if you're just in case you're wondering an abstract class is a class that literally would never be called by itself it's only used for inheritance and it's used quite a bit in other programming languages. But when I mean abstract I mean a parent class that other classes can inherit from.
So this one is going to look a lot like our invoice class in the fact that it's going to have a company profile attribute and it's going to be of type string. And it's also going to have a constructor. But this one for the sake of brevity I'm going to only give one parameter so as a constructor name and this time I am going to pass data types to it says is going to be a string and I'm going to then set just so you can see that we're setting the company profile and setting it equal to name. Now we could instantiate this report class just like we did with the invoice. But instead what I'm going to do is let's imagine a scenario where we need to create reports but we also need to create specialized reports for a program. So one thing that we would need to do is create an invoice So in order to create an invoice class that we want to inherit from report I'm going to say class invoice and then extends report. So what this is going to do is this is going to create a class for us but it's going to inherit all of the data in the behavior of the report class. So that's going to be our Invoice class.
class Report { companyProfile : string; constructor(public name : string) { this.company Profile = name; } }
And because it would be pointless to only inherit one class you always have one to have at least two classes because if not then you didn't need to use inheritance you could put everything in the single class. But let's say that we have something called Bill of lading and if you've ever built any programs or you've ever done anything in the shipping industry a bill of lading is something that is required for many shipments. So these are both natural to fit under a report class. Now inside of these now we can create a constructor. And now this is where you're going to see some of the beauty of inheritance because if you notice up top we have a name but in an invoice let's say that we want to have a name but we also want to have a total to put on the invoice.
So I'm going to say public in name because we still do want to retain that name and it was going to be a data type string but then we also want total to say public total and this is going to be of type number. And as you notice we have quite a few errors here but the reason for this is something I'll show you how we can fix it shortly. If you click on it you can see that the constructor implementation is missing. And so what that means is that we need to place this code in here. I'm actually going to put this on one line because it's going to be very short. And now inside of it say super in in parentheses name and after name on a semi-colon. So what exactly is all this?
class Invoice extends Report { constructor(public name : string, public total : number) { super(name); } }
If you've never used inheritance before this may look a little weird. You do notice that we got all of our errors fixed which is good but what I'm doing here is we're taking the invoice we're saying that this invoice is a child class of the report. Another way of saying this and it's to me it's one of the more intuitive ways of saying it is that an invoice is a type of report and if you can't say that your subclass or your child class is a type of the parent class then you might be designing it the wrong way. Because this is the way it should work. An invoice is a specialized type of a report. So this is the inheritance part. The constructor is a constructor just like the one right here. So this constructor is going to be run when we run the code and you need in the constructor you need to include the name because we want the name and we're passing the data type. And then notice here we're passing in another argument. And so with this one this is where it starts to get more specialized. Our report didn't need to have a total but our invoice does. So we're adding a second argument. Now this is where this might look a little tricky and a little bit weird but just follow along I promise. It's actually pretty straightforward.
So inside the brackets up here you can see we have a constructor function and this is sateen the company profile equal to the name. Now here what are constructor's doing. And you could put this on multiple lines. I don't want to use single line syntax to confuse you at all. We can put this on multiple lines. I'm going to put on one just so you can see it all at the same time and this is pretty much the common convention if you only have a small amount of code. And so what super is doing is super. You could also just in your mind say that this is saying parent which means that I want to call the parent class and I'm going to pass in name. So what it's saying is I want this name argument this name attribute. I want this to be inherited down so that I can use it inside of this invoice. So that's all it's saying is that call the parent class. It has already created a name attribute. And we want to have that one associated with our invoice class.
Now I'm going to come down and do the same thing for Bill of Lading but bill of lading are more specific. They need some more attributes. For one we don't care about it total here. All we care about is some other things so we're going to carry a care about a city which is of type string and then we also want to have a state which is also going to be of type string. Once again all we want from the parent class from a data perspective is the name. Now, these are both fine and this but this would all work and let's actually confirm that that's true. I'm going to come down here and say invoice equals new invoice and I'm going to pass in a name and a total which has to be a number, and then end it in a semi-colon and now it needs a bar. Sorry, my Ruby was getting in the way. And now let's say console log invoice and because we have access to company profile this should work.
class BillOfLading extends Report { constructor(public name : string, public city : string, public state : string) { super(name); } } var invoice = new Invoice(‘Google’, 200);
So now let's run this code node 021_inheritance. You can see that that worked in that printed out the company profile so all of this is working. We have access from the invoice class because it's a child class we have access to the publicly available data up in the report class. So that's all good but now we can get a lot better and we can create our own methods that are specific to our child classes. So actually public is assumed I could say public. Each one of these like company profiles up here we could say public but by default, if you don't put public. It's going to be it's going to assume that you're public and I'll go into some of the differences on what public private protected and what all of those mean at a later episode.
Ok, so here I'm going to create a method called print invoice. It's not going to take any parameters. And inside of that I just want to take I want to return this name and then separate that out by a comma and add this .total to it. And so what this is going to do is this is a function inside of the invoice class or report class does not have access to it only invoices do. And what it's going to do is it's going to take the values of name and total for whatever's created. Remember that this means it's talking about the object that is created this has access to those parameters. So when they're saying this name it means this name meaning Google. If we create invoice 2 and change this to Yahoo. And this value to 2000 this will this name will mean Yahoo. When we talk about this version of it when we talk about this version this instantiated version this is going to equal Google. So this just means that it is taught it is referencing the values specific to the item or the object that's been created. So that's what this is just as a review I know this can be really confusing if you never used it before. So we have this not name Khama this total. And in order to call this, we just come down here print invoice make sure to put friends around it and let's run the code. And there you go it prints out Google and 200 just like it should I’m going to get rid of this yahoo one because only one work with one invoice. So this is working perfectly.
class Invoice extends Report { constructor(public name : string, public total : number) { super(name); } printInvoice() { return this.name + “, “ + this.total; } } var invoice = new Invoice(‘Google’, 200); var invoiceTwo = Invoice(‘Yahoo’, 2000); console.log(invoice.printInvoice());
Now let's come and do the same thing to our Bill of Lading now we have a bill of lading and I'm going to create a method here called Print well and it's going to do the name. But notice here we're already getting an error which we should because we only want to have a city and state so we could say the city and then add in the state. And now this is an invoice. Here's a bol well and the second we change this it's going to throw an error because we need other parameters. We need a name a city and a state so we can say Scottsdale and then Arizona and they get a duplicate or console log and they're going to print them out. Everything looks good there from a syntax point of view. Now if I run this you can see that it's got a weird little error there. Let's see what the problem is. We have public name city and state. We're bringing in the super name this is the issue. I had a comment right after city run the code again and you can see that worked perfectly.
class BillOfLading extends Report { constructor(public name : string, public city : string, public state : string) { super(name); } printBol() { return this.name + “, “ + this.city, + “, “ + this.state; } } var invoice = new Invoice(‘Google’, 200); var invoiceTwo = BillOfLading(‘Google’, ‘Scottsdale’, ‘AZ’); console.log(invoice.printInvoice()); console.log(bol.printBol());
So that is how you can implement inheritance. The key word is extends but even more important than the syntax is the concept and what it's actually doing which is it is inheriting behavior from parent classes and it allows you to share behavior and data with other more specialized classes because Could you imagine if you did not use inheritance here and you simply used Invoice and Bill of Lading you'd have had to put all of this code in each one of these and had duplicate code which would violate the do not repeat yourself policy and you would end up having to make lots of changes in your code every time a single requirement change was made. And it would get very messy. So that's a beautiful thing about using typescript as opposed to pure JavaScript is you're able to work and leverage these object-oriented behavior attributes and you can write cleaner code and you while still taking advantage of all of the power of what JavaScript offers. So that is how to use inheritance in typescript programs.