Learn TypeScript #6, Advanced Classes

Transcript:

Hello, everybody! In the previous video we talked about the basics of classes, today we’re going to go a bit more in-depth into the more advanced features of classes. So, let’s get started!

Starting off simple, if you saw the video on interfaces, you’ll remember the “readonly” property. Just like in interfaces, you can mark class fields as “readonly” if you want them to stay constant.

While it may be “read only”, it needs to get an initial value from somewhere. And that somewhere is either in-line here when you declare the field or here in the constructor.

If we try to create a function that changes the name,

You’ll see we get an error “cannot assign to name because it is a read-only property”, which is correct.

So, if there’s a value you want to keep constant throughout an object’s lifetime, readonly is your way of doing that. However, as I mentioned in the previous video on interfaces, read-only is only enforced at compilation time by TypeScript.

Nothing in JavaScript is going to prevent a readonly field from being modified. So if you’re interacting with non-TypeScript code, or with TypeScript code that uses the “any” type a lot, there is a chance that your read only fields could get changed.

Moving on from “readonly” properties, let’s talk about static properties.

If you’re familiar with the term “static” from physics, this is nothing like that at all. “static” is a fancy keyword that lets us define properties on the class itself, rather than on the instances of the class.

For example, we might want to keep track of the number of Player instances that we have created. We could do this by creating a static playerCount field, and then incrementing that by one in the Player constructor.

If we scroll down in the code a little and try to access “p.playerCount”, you’ll see that is an error.

To actually access the the player count, we’ll need to do capital P Player.playerCount.

Static properties are helpful for anything that you want to share across all instances of a class. Frequently you’ll see them used to contain constants that are the same across all instances.

For example, players might have a maximum speed that they can move at, so we could create a static “maxSpeed” field to hold that value.

Similarly to normal fields, static fields can be read only so if maxSpeed is never going to change throughout the course of the game, then it would make sense to mark it as read only to ensure that we don’t accidentally change it in some other part of our program.

Now, if you’ve used any other object-oriented programming language such as Java or C#, you’re probably familiar with public, private, and protected members of a class. TypeScript has the same concepts.

In the previous video, I mentioned that all fields and methods are publicly accessible by default, and that means that the “public” keyword is implied on every field and method.

So while we could put the “public” keyword in front of all of these definitions, there’s no real need to do, since it’s equivalent to putting nothing there at all.

Now, one of the principles of good object-oriented design is that you should provide a simple, clean public interface to your object, and hide all the gory implementation details that aren’t relevant to other parts of your program.

This makes it easier to change the internal workings of a class without worrying about breaking any other part of your program, and it prevents internal methods from being misused.

To illustrate this, let’s imagine that for every frame in our game, we want objects to be able to run any logic they need to run to keep the game moving forward. To facilitate this, we’ll change our GameObject interface to include an “update” method that will contain this logic.

So, our Player class is going to need to implement this update method.

Now in order to properly move the player, there might be some complicated physics calculations that we need to run. Let’s create a “calculateMotion” method to represent that.

The logic in “calculateMotion” won’t be relevant to any other object in the game, and we don’t want anybody accidentally calling it and doing something like moving our player twice, or something else equally as weird. So it makes sense to mark it as “private”.

The “private” keyword makes it so that only the class itself can use the method or field. If we scroll down and try to call “calculateMotion” on the instance of our Player class, TypeScript gives us an error saying it’s only accessible within the class itself.

It’s important to understand that private fields and methods don’t count towards whether or not something implements an interface. Say if we set the “z” variable to private, we’ll get an error that Player incorrectly implements the GameObject interface.

Even though technically the Player has that field, nobody can use it and so TypeScript correctly calls that an error.

One other thing to note is that similar to readonly, the public-private distinction is only handled by TypeScript at compile time.

If we open up our compiled JavaScript file, you can see we’ve got our “calculateMotion” method and we’ve got our “z” variable and there’s nothing different about these compared to anything else in this file.

Nothing in the resulting JavaScript code is actually enforcing that these properties are private. TypeScript will prevent you from accessing them improperly in your TypeScript code, but when we get to the JavaScript runtime, everything is public and accessible.

This is helpful for debugging, but if you’ve got a situation where one TypeScript team of developers is working with another plain JavaScript team of developers, you’ll to make sure to communicate what’s private and what’s public.

Alright, I mentioned earlier that there was one other level between public and private, and that’s “protected”.

A “protected” field or a “protected” method is similar to private in that only the class itself can access the property, but with the added distinction that if you have another class which inherits from your class, that child class is able to access the property without restriction.

We haven’t talked about inheritance yet, so I’ll show you a more concrete example of this in a few minutes.

One other thing that I wanted to mention before we move on, is an alternate way of writing what we’re doing with the name field.

Currently we’re declaring it as a public field, and we’re setting its value directly from the constructor argument.

This is actually a really common pattern, and so TypeScript has a way of doing it in 1 line rather than 3.

Let’s get rid of our declaration line, and our “this.name = name” line, and instead we’ll simply put the “public” keyword in front of the name argument.

This has the exact same effect as the 3 lines we had earlier: “name” is declared as a public property, and when it’s passed into the constructor as an argument, it gets directly assigned from that argument value.

This is exactly equivalent to what we had earlier.

Just to be clear, this works for any of the accessibility modifiers except for “static”, so if we wanted this to be “protected readonly” instead of “public”, that is a perfectly valid thing to do.

If we run our program, you’ll see everything is doing exactly what we’d expect.

Okay, on to inheritance!

At a high level, inheritance is a feature that let’s you create new classes that are based on the implementation of existing (parent) classes.

For an example of how to use inheritance, let’s say that for the game we’re making, we’ll have different types of players that share some common functionality.

So our existing Player class is where all the common functionality will be stored, and then we’ll create more specific classes for each type of player that will include whatever differentiates that specific type.

Let’s say that in our game, one type of Player will be able to shoot at enemies with guns. To support this, we’ll create a Gunner class.

We’ll write class Gunner extends Player.

And what this will do is cause Gunner to “inherit” all the fields and methods that the Player has.

Gunner will have an x, y, and a z position. An update method. And so on. Anything new we add to Player will also end up as a part of Gunner.

Even the constructor is carried over, so we could swap out our usage of new Player to new Gunner. And if we go to our Terminal and run that.

You can see Node.js telling us this is now an instance of the Gunner class, but otherwise everything else is working exactly the same as it did thanks to inheritance. Since Gunner has everything Player does, we can treat our Gunner exactly like a Player.

Going back to Gunner’s implementation, we can add things to this in exactly the same way we can add fields or methods to any other class.

For example, we could add a field that keeps track of the number of bullets.

Let’s say that in addition to the name of the Gunner, we want the type of the gun to be specified when a new Gunner is created. We’ll start by creating a new constructor that takes both a name and a gunType.

You’ll see that TypeScript is giving me an error “Constructors for derived classes must contain a super call”. What does that mean?

It means that in order for our object to be properly constructed, we must call the constructor of our parent Player class. You can do that by using the super keyword, and then we’ll give it whatever arguments that the Player constructor needs. Here we’ll just pass in our name, and everything will be happy.

The super keyword is also used to refer in general to any other method you may want to call on the parent class.

Say, for example, we wanted to have our own custom update function for our Gunner. Perhaps every frame they should gain an additional bullet that they can shoot.

But, we still want to use the same movement logic that is defined in our parent class. So we’ll need to call Player.update(). We can do this by adding a call to super.update() as part of our update method, and then that original method will be called.

It’s important to note that that without the super.update() call, the Gunner class would have it’s own completely isolated implementation of update(), the original update code defined in the Player class would go unused in Gunner.

This is often a valid option, as some classes may want to completely override whatever functionality their parent class had implemented.

Previously we mentioned protected fields as an alternative to private fields. Let’s show an example of that here. We’ll change our calculateMotion() method to be protected.

And you’ll see if we try to call calculateMotion() within our Gunner’s update method, that’s perfectly acceptable.

However, if we change calculateMotion() to be private instead of protected, Gunner’s use of that method becomes an error.

So, the protected modifier lets subclasses access the field or method, private keeps it so that only the things within the bounds of that original class definition can access it. Both private and protected prevent access from any other part of the program.

Another inheritance feature that is frequently used is something called an “abstract class”.

An abstract class is a class that’s not meant to be used on its own.

As we said earlier, this Player class is meant to contain functionality common to all player types, but we’ll probably never want to create just a generic player.

This means it’s a good candidate to become an abstract class.

If we add the abstract keyword, you’ll see down below in our code we get an error where we’re directly creating an instance of Player. You cannot create an instance of an abstract thing. We’ll have to switch that to Gunner.

The abstract keyword also comes into play in one other place: method declarations.

We can put the abstract keyword in front of our update method, and this will indicate that this is something we want all child classes to implement, but the parent class won’t be providing any sort of default functionality itself.

If we go this route for our game, this is basically equivalent to saying that we know every different type of Player will need to update itself in some way. But the exact details of how they update will be so varied, that there’s no point in providing some sort of default implementation.

Just to show you that this is being enforced, if I remove the update method on Gunner, you’ll see that we are now getting a complaint about “Non-abstract class Gunner does not implement inherited abstract member update from class Player”.

And that wraps it up for this video!

If you’re interested in going deeper with TypeScript, I’m working on a course called Learn TypeScript by Example.

You can find it at https://typescriptbyexample.com If you scroll down to the bottom, you can put in your email address and I will let you know when the course is released!

Thank you so much for watching! I will see you in the next video.

Programming With Ruby Episode 17, Getting Advanced

Covered In This Episode:

  • Symbols
  • eval
  • Bindings
  • Running Other Programs
  • Safe Levels

Transcript:

Hello Everybody and welcome to Programming With Ruby Episode 17,
Getting Advanced. I’m Tyler, and this video is brought to you by
manwithcode.com.

By now, you know a large amount about Ruby, so in this episode we will
be going over some advanced features that Ruby has.

More specifically I will be teaching you what Symbols are, and when to
use them. I will be showing you how to use eval, and how to use
bindings with eval. You will learn how to run other programs from
Ruby. Finally I will show you what safe levels are.

Lets get started!

Symbols

Symbols are a type of variable that are very much like strings, but
more lightweight. Symbols look like this:

:variable

Symbols can’t be manipulated like strings can, which seems like a huge
drawback, but they do have a couple benefits.

Each time you use a string, to say access a hash. Ruby creates an
instance of that string. Where if you use a symbol, it is only ever
instanced once. Meaning that the use of symbols will take up less
memory than strings will, if you are, say accessing a hash many times.

Symbols are also slightly easier to type since the colon is on the
home row on US keyboards.

eval

eval is a way of running Ruby code that is contained in a string. For
example, lets say you have a string that looks like this:

"puts 'Hello World'"

It is just a simple string, so it does nothing. But, if you use the
method eval on that string it will execute the code inside. So this
will print Hello World! on to the screen:

eval "puts 'Hello World!'"

This isn’t always useful, but you can use it if you want your users to
be able to enter Ruby code into your program.

You can also pass bindings to eval. So if we had this method

def my_method my_binding
    eval "puts x", my_binding
end

x = 5
my_method binding

This outputs:

5

Some of you may notice that the variable x isn’t defined in the method my_method. By using the binding method, we can make variable scopes portable!

Running Other Programs

There comes a time when you will want to be able to run a program from
Ruby, maybe you want to automate something, or simply get the output
from an external program.

There are a few ways of doing this.

The first is with the method exec, which runs an external programs,
and quits the Ruby script at the same time:

exec('ls') # dir on windows
# Program never gets here

There is also system, which does the same thing, but doesn’t quit the
Ruby script, and returns true or false if the program was successful:

system('ls') # dir on windows
# we do get this far

Finally we have the “back-tick” `. Which looks like a sideways single
quote. On my keyboard it is above the tab key. You surround your
command in the back-ticks, like you would for a sting. Unlike the other
two methods of running a program, this method also returns the output
of the program you run.

variable = `ls`

Safe Levels

If you are running a Ruby interpreter online or in another environment
where users can enter in and run Ruby code. They hold the ability to
wreak havoc on your system.

The way to prevent this from happening is by using safe levels. Safe
levels are a way of preventing the user from getting access to the
file system, or changing any variables that the program has.

You set safe levels by setting the $SAFE variable. By default it is
set to zero.

$SAFE = 4

Ruby “taints” objects that could be dangerous.

There are five different safe levels.
0 => The default, you can do anything
1 => Can’t use environment variable, eval, load, require, and more.
2 => Same as above and also can’t use files
3 => All objects created are tainted, can’t be untainted
4 => You can do almost nothing… Can’t modify the untainted, can’t
use exit. Basically completely safe and sand-boxed.

That brings us to the end of the episode. If you liked these videos,
please donate. It costs me in both money and time to make them.

If you have any questions, comments, or suggestions please don’t
hesitate to leave a comment on this page or email me at
tyler@manwithcode.com

Thanks for watching, goodbye!