Now in Javascript, we can work with classes to define blueprints for objects, right?
And of course we can use classes in Typescript as well, it supports this feature as well
and since it compiles code to code that works in older browsers as well,
it even compiles class code down to Javascript code that does not use classes but constructor functions
and runs in older browsers as well.
Now let's stick to this file and let's say in there, maybe at the very top
but that does not matter,
we want to define a class for a user in our app.
Now this is how we can define a class
and now in this class, we can add a constructor function and
we can expect a name which is of type string and an age which is of type number let's say
and then here we say this name equals name and this age equals age and now we would have these two properties
here in our class and already, we get an error, property name does not exist on type user.
Now in vanilla Javascript, this would be a valid syntax we could use, not so in Typescript, instead there
we have to add property as fields all the time, every time we want to work with a property. In Javascript,
the field syntax is pretty new and experimental and not supported by all browsers but since Typescript
gets compiled down to Javascript code which will work in all browsers, Typescript can enforce certain
features which wouldn't work in browsers otherwise
and here we have to simply add name and age as fields up there so that we can work with them in here
in the constructor.
This is just how Typescript works,
it forces us to use this setup because this in the end just leads to cleaner code,
other developers reading our code can quickly see which fields, which properties this user class will have and
of course for this simple class,
it wouldn't have been too hard by just looking at the constructor
but if this is a bigger class with a lot of properties that get added everywhere throughout the class,
having a list of properties right at the top can be very greetable and very helpful.
So that's how we can define such a class in Typescript.
Now in addition, Typescript also gives us access modifiers, we can make it clear whether a property is
intended to be used from only inside the class or also from outside of it
and there for example, we could lock down the age to be used from only inside by adding private.
The opposite is public but you don't need to add this because it's a default anyways, so you can add it
but you don't have to.
Now age is really only usable from inside this class,
if I create a user here with new user and I feed in Max and 30, I can console log user.name but if
I try to access .age, I get an error.
Now technically, Javascript of course doesn't really have private properties or at least it's a very
new feature which is not supported everywhere,
so technically once this is compiled to Javascript, this privateness is lost,
it's just a Typescript feature so that in your Typescript code, you can't access this and you get an error.
So the developers writing code know okay I shouldn't access this property,
this code is not the code I should write,
that's the idea here,
using the name is fine because that is public of course. And now there also is a little shortcut in Typescript,
we can set up our class like this but I'll copy it here and comment it out because for this common scenario
where you have a couple of fields and then you assign values to them in the constructor, you can shorten
this and actually get rid of these fields and instead add the access modifiers here in front of the parameters
in the constructor
and that's simply a special feature available in the constructor of a class.
By doing this, you tell Typescript these parameters should also be automatically converted to properties
of this class and therefore we can also get rid of these lines.
So this class will do exactly the same thing as this class did, simply because of this shorthand Typescript
supports to you,
this makes it clear that Typescript should add a name property to this class and the age property and the
value passed in for the name parameter should be stored in the name property, the value passed in for
the age parameter should be stored in the age property and the name property will be public,
the age property will be private, so the same thing we have up there but a bit easier,
that's a huge advantage.
So this is how we can work with a class here.
Now of course in Typescript, just as in vanilla Javascript, we can also inherit. We can add an admin class
which extends the user class let's say
and this now simply allows us to add additional fields, for example here in the constructor,
we could also have a private or public, whatever you want, property named permissions
and this let's say is an array of strings, whatever you want
and now we just have to call super in here and therefore also accept a name here and an age, in this case
without using the shortcut because I will forward name and age to the super constructor, so of the base
class and also add my own property here, this private permissions property which holds an array of strings.
So this is how we can extend a class, how we can still use this shortcut and call super which we
do have to do if we extend a base class and bring our own constructor and now we can of course work
with either that class or that class, just the way you are used to from vanilla Javascript classes in
the end. Now kind of related to classes but also different is a Typescript exclusive feature called interfaces.
Now when you define a class, you have a blueprint for an object right
and you can always instantiate a class as we're doing it down there.
Now sometimes you just want a blueprint but you don't want to instantiate it because it's very simple,
it doesn't have any methods and you just don't want to call new and create a object like this but create it
with the curly braces and still have that extra type safety.
Now in such a scenario, you can of course use an alias, here for example I do store an object in there
but you could also use an interface, an interface maybe named calculation container here.
You create it with the interface keyword, then any name of your choice which should start with a capital
character
just as for custom types and classes and in there you could define how an object should look like,
that you have a res property which is a number and then maybe also a print method which returns nothing,
this is how you then define it here. You could also use this syntax if you wanted to but I will go for
the method syntax.
So now this is an interface and we can't use new with it, new with calculation
container will not work but we can use it as a type,
for example here we can use the calculation container and have an array of calculation containers and
our other code will work because we define in there we want to have an object with this structure
and that's exactly what we create down there.
So you can use this as a blueprint for objects which you create with curly braces instead of with the
new keyword
but like this, it's simply just an alternative to the type keyword because this is basically the same
as if we would have set type calculation container equals this,
this is also something we could have done.
So why do we have interfaces
if they're just an alternative to the type keyword? Well besides using them for object type definitions,
you can also use them as contracts in classes.
What do I mean by that?
Well we can define an interface, maybe name it greetable, where we say we want to have a name which is
of type string and now in our class we can do something which is called implement the interface and
that's a pure Typescript feature,
this does not exist in vanilla Javascript.
Now what does this do?
It forces this user class to have a public name property which is of type string and if I would remove
this here, this parameter property assignment, I correctly would get an error that the class user incorrectly
implements the interface greetable,
that's why I refer to interfaces also as contracts.
Besides using them as blueprints for objects, you can also use them to force classes to have a certain
structure, to bring certain properties,
classes can then always add more than what the interface demands but never less. Now you might wonder why
would you do that,
you are the developer, you are writing the code,
why would you force yourself to build a class in a certain way.
If you want to do that, you can just do that.
Well for one, keep in mind if you're working in bigger projects, it can be hard to keep track of what
you want to do where,
also if you're working in a team it can be helpful, to force other developers to do something in a certain
way
so that you all use the same style and you're working on the same goal
and here it might get clearer if we maybe create another interface, printable,
you don't have to name it like this but that's a convention you will often see and there you say that
you want to have a print method,
then you could implement this as well.
You can implement multiple interfaces by simply separating them with a comma
and now you would be forced to add a print method here which does something, we don't know
what but it does something.
Now we can make it clear that every class which has a printable interface, that implements a printable
interface needs to have a print method and this can give us extra type safety because maybe we got another
part in our code where we are not interested in the exact type of object we're working with, we're not
interested in the fact whether it's a user or an admin or a horse
but we are interested in the fact that we can call print. In such a scenario,
we could simply assign to type printable to the parameter or variable that stores our object and it
would be clear that whatever is stored in there will have a print method which we can call.
So this is why we can use interfaces as contracts to give us extra type safety and guarantee that certain
classes and therefore the new objects built on those classes have a certain structure, have certain methods
and that is something you will also see a lot when you are working with Typescript.
Now I do want to emphasize that if you compile this file, interfaces are totally lost. We see a lot of code
here because it was compiled to code that doesn't use the class keyword, hence we don't see the class
keyword here
but we won't see the interface here, so this is a pure Typescript feature which gives us extra type
safety whilst we're working in Typescript.