Lesson Weekend

So far we've learned how to declare specific data types for variables and method parameters in Typescript. We've also learned how to compile a TypeScript file and how this will help us catch errors.

However, TypeScript's benefits extend beyond type declarations! In this lesson, we'll learn how to declare entire classes in TypeScript. As you already know, classes and objects help organize our code.

Class and Object Review

In Intro to Programming, we created JavaScript constructors like this:

JavaScript
function Task(description, priority) {
  this.description = description;
  this.priority = priority
}

However, in your level two course (if you took Ruby, C#, Java or PHP), you learned to create objects using constructors and classes like this:

Ruby
class Task
  attr_reader(:description, :priority)

  define_method(:initialize) do |attributes|
    @description = attributes.fetch(:description)
    @priority = attributes.fetch(:priority)
  end

end
C#
class Task
{
  public string Description;
  public string Priority;

  public Task(string description, string priority)
  {
    Description = description;
    Priority = priority;
  }
}
Java
class Task {
  public String mDescription;
  public String mPriority;

  public Task(String description, String priority) {
    mDescription = description;
    mPriority = priority;
   }
}
PHP
<?php
    class Task
    {
        private $description;
        Private $priority;

        function __construct($description, $priority)
        {
            $this->description = $description;
            $this->priority = $priority;
        }
   }
?>

Classes are blueprints for objects. They allow us to define what properties each object will have and declare what actions (ie: methods) they can perform. Remember, all objects are instances of a class.

Further JavaScript Object Review

If you'd like to review these concepts before moving on, revisit the following lessons from Intro to Programming:

The TypeScript syntax to declare classes and objects will differ from the syntax in the lessons above. However, the general information about objects and classes and along with their purpose still applies.

Constructors versus Classes

We previously used Constructor methods to create objects in JavaScript. Now we're creating entire Classes. What's the difference?

Essentially, these are two different ways to organize our code. The newest version of JavaScript (ES6) includes support for declaring classes natively in JavaScript. However, not all browsers support ES6 yet.

TypeScript allows us to declare classes that will be compiled into universally-supported ES5. This allows us to both take advantage of new JavaScript capabilities and write code that can be supported in almost any modern browser.

Declaring TypeScript Classes

Let's write a To Do List application to walk through the process of declaring and using a TypeScript class.

Note on To Do Lists

Some students may roll their eyes at the idea of creating yet another To Do List. After all, we've already created them in previous courses. Keep in mind that this repetition is intentional for a few different reasons:

  • We already understand the structure of a To Do List application. We know what it should look like and what it should do. This allows us to focus entirely on new TypeScript concepts.

  • The concepts required to create a To Do List are very general and can be applied to other applications. You can use this example application as reference material when creating TypeScript classes in the future.

Declaring a Class

Let's create a new project folder called typescript-to-do. It should contain an app directory with a to-do.ts file inside. We'll declare our Task class in this file:

typescript-to-do/app/to-do.ts
class Task {
}

Similar to other languages, we use the class keyword, followed by the name of the class we're creating. Class names always begin with a capital letter (even across languages and stacks!), and are followed by a set of curly braces, where properties and methods will reside.

Class Properties

Next, let's add properties to our class. Each Task object will contain the following:

  • A description property, containing a brief string description of the Task.
  • A priority property, containing string information on how urgent the Task is, like "High", "Low", or "Medium".
  • a done property containing a boolean denoting whether this Task is complete.

We'll assign each property a specific data type:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean;
  description: string;
  priority: string;
}

Constructors

Next, we'll need a constructor method to instantiate new Tasks:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean;
  description: string;
  priority: string;

  constructor(descriptionParameter: string, priorityParameter: string){
   this.done = false;
   this.description = descriptionParameter;
   this.priority = priorityParameter;
  }
}

The keyword constructor is special. Both TypeScript and JavaScript know this method is meant to initialize new instances of our class.

Our constructor method takes two parameters: descriptionParameter and priorityParameter. It does not require information about the done property because all Task objects will begin with a default done property of false.

Let's test it in the console. We'll create an index.html file to run our script in the browser:

typescript-to-do/index.html
<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="app/to-do.js"></script>
    <title>TypeScript To Do List</title>
  </head>
  <body>
  </body>
</html>

Remember, we link the file app/to-do.js in our <script> tag, NOT app.to-do.ts.

Next, let's begin creating Task objects and printing them to the console:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean;
  description: string;
  priority: string;

  constructor(descriptionParameter: string, priorityParameter: string){
   this.done = false;
   this.description = descriptionParameter;
   this.priority = priorityParameter;
  }
}

var tasks: Task[] = [];
tasks.push(new Task('Do the dishes.', 'Medium'));
console.log(tasks);

Note that TypeScript requires you define strings with single quotes('string!'). You will receive errors if your strings contain double quotes ("string!").

  • In the code above, we create a variable named tasks and we set its data type to Task[]. The [] part refers to an array. The Task part denotes the specific type of data the array will contain. Therefore, the data type Task[] refers to an array containing Task objects.
  • We create a new Task object. The new keyword will automatically invoke our constructor.
  • We push our new Task to the tasks array and log the array to the console.

Let's compile:

$ tsc app/to-do.ts

If we run index.html in the browser and open the console, we can see our tasks array contains one Task:

first-task

Constructor Shortcuts with public

Let's address a very important TypeScript shortcut before moving on. If we add the word public to our constructor() parameter list, like this:

typescript-to-do/app/to-do.ts
class Task {
  ...
   constructor(public descriptionParameter: string, public priorityParameter: string){
    this.done = false;
    this.description = descriptionParameter;
    this.priority = priorityParameter;
}
...

...then, make sure the names of parameters in the constructor() method match the names of the properties at the top of the file exactly, like this:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean;
  description: string;
  priority: string;


  constructor(public description: string, public priority: string){
    this.done = false;
    this.description = description;
    this.priority = priority;
  }

  ...

}

We won't have to manually assign the constructor() method's parameters to Task properties! We can remove lines like this.description = description; from our constructor, like this:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean;
  description: string;
  priority: string;


   constructor(public description: string, public priority: string){
    this.done = false;
  }
...

If constructor() parameters share the exact same name as class properties (like description, or priority in the example above), TypeScript will automatically assign them to a public property of the same name and type.

Including public parameters with the same names as class properties in the constructor actually creates these properties for us. So, we can remove any properties included in constructor parameters from the top of the file, like so:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean;

  constructor(public description: string, public priority: string){
    this.done = false;
  }

...

}

Additionally, we don't have to declare our done property as a boolean under the class declaration, then later set it to false in the constructor. We can refactor this process into one line, like this:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean = false;


  constructor(public description: string, public priority: string){}
...

This is because we can declare class properties the same way that we declare regular variables. Remember this?

var greeting: string = "Hi TypeScript!";

We can use this same structure to provide a default value for a class property:

class Thing {
  propertyName: type = defaultValue;
}

Access Level Modifiers

Including the term public before a variable or property simply means any part of the application has permission to access this property. That means TypeScript has permission to automatically access and assign it to class properties.

This is known as a modifier or access-level modifier. Other modifiers include private and protected. These are common in strictly-typed languages like Java and C#.

Don't worry too much about this concept quite yet. We'll declare everything as public for now. If you'd like to (optionally) explore access level modifiers in TypeScript further, you can check out the TypeScript documentation on Classes.

Terminology


  • Modifier or Access-Level Modifier: The term private, public or protected that sometimes precede a variable declaration in TypeScript. These control how much of the application is permitted to access certain variables. We'll declare everything public for now.

Overview


  • TypeScript allows us to create custom classes, just like you've done in PHP, Ruby, or C#.

Tips


  • If constructor() parameters share the exact same name as class properties TypeScript will automatically assign them to a public property of the same name and type.

Examples


Here's an example TypeScript class:

typescript-to-do/app/to-do.ts
class Task {
  done: boolean;

  constructor(public description: string, public priority: string){
    this.done = false;
  }

}
  • The description and priority parameters in the constructor() method will automatically create class properties of the same name.
  • Including the code this.done = false creates all Task objects with a default done property of false.
  • Because done is not included in the constructor's arguments, TypeScript does not automatically create a class property for it, so we must manually create one under the class declaration.

Additional Resources