Introduction to TypeScript Classes — Static Properties, Abstract Classes and More
Tags: typescript, webdev, programming, javascript
Published: 2021-01-23 19:58:20

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

Classes in TypeScript, as in JavaScript, are a special syntax for its prototypical inheritance model that’s a comparable inheritance in class-based object oriented languages. Classes are just special functions added to ES6 that are meant to mimic the `class` keyword from these other languages.

In JavaScript, we can have `class` declarations and `class` expressions because they’re just functions. So like all other functions, there are function declarations and function expressions. This is the same with TypeScript. Classes serve as templates to create new objects. TypeScript extends the syntax of classes of JavaScript and then adds its own twist to it. In this piece, we’ll look at static properties, abstract classes, and constructor functions.

* * *

Static Properties
=================

With TypeScript, we can designate members as instance variables, which don’t have the keyword `static` before them, and static members, which have the keyword `static` keyword before them.

Static members can be accessed without having the class instantiated. Of course, this is given to the access modifiers that are designated for the member. So public static members can be accessed directly from outside the class. Private static members can only be used within the class, and protected members can be accessed by a class the member is defined in and also by subclasses of that class.

The `static` keyword can be used by both fields and methods. For example, we can use it like in the following code:

```
class Person {  
  static numPeople = 0;    
  constructor(name: string) {  
    Person.numPeople++;  
  }  
  static getNumPeople() {  
    return this.numPeople;  
  }  
}

const john = new Person('John');  
const jane = new Person('Jane');  
console.log(Person.numPeople);  
console.log(Person.getNumPeople());
```

In the code above, every time we instantiate the `Person` class, we increase the static property `numPeople` by one. Since static properties are shared by all instances of the class and don’t belong to any one instance, we can access `numPeople` directly by using `Person.numPeople`.

Likewise, we have a static method, `getNumPeople`, we can call directly without instantiating the `Person` class. Therefore, when we get the `numPeople` by using `Person.numPeople` and call the `Person.getNumPeople()`, then the value 2 is returned for both.

Since the members are static, the values is part of a class and not part of any instance, so even if the instances are destroyed the values will be kept. This is different from instance variables, which are accessed from the `this` inside the class and the variable with the instance of the class outside the class.

* * *

Abstract Classes
================

TypeScript has abstract classes, which are classes that have partial implementation of a class and in which other classes can be derived from. They can’t be instantiated directly.

Unlike interfaces, abstract classes can have implementation details for their members. To declare an abstract class, we can use the `abstract` keyword. We can also use the `abstract` keyword for methods to declare abstract methods, which are implemented by classes that derive from an abstract class.

Abstract methods don’t contain implementations of the method. It’s up to the subclasses that derive from the abstract class to implement the method listed. They may also, optionally, include access modifiers.

We can use abstract classes and methods as demonstrated in the following code:

```
abstract class Person {  
  name: string;  
  age: number;  
  constructor(name: string, age: number) {  
    this.name = name;  
    this.age = age;  
  } abstract getName(): string;  
  abstract getAge(): number;  
}

class Employee extends Person{  
  constructor(name: string, age: number) {  
    super(name, age);  
  } 

  getName() {  
    return this.name;  
  } 

  getAge() {  
    return this.age;  
  }  
}

let employee = new Employee('Jane', 20);  
console.log(employee.getName());  
console.log(employee.getAge());
```

In the code above, we have the abstract `Person` class, which has the abstract methods `getName` and `getAge`.

As we can see, the abstract methods only have signatures in them. The actual implementation of the methods are in the `Employee` class, which extends the `Person` class.

We have the actual implementation of the `getName` and `getAge` methods in the `Employee` class.

TypeScript checks the method signature and the return type of the class, so we must be implementing the abstract methods as it’s outlined in the abstract class. This means that in the example above, the `getName` method must take no parameters and must return a string.

Likewise, the `getAge` method must take no parameters and must return a number. After the abstract methods have been implemented, we can call them normally like any other method.


* * *

Constructor Functions
=====================

When we declare a TypeScript, we’re declaring multiple things at the same time. We’re declaring an instance of the class, which is the entity with the code preceded with the `class` keyword. For example, we can write:

```
class Employee{  
  name: string;  
  age: number;  
  constructor(name: string, age: number) {  
    this.name = name;  
    this.age = age;  
  }
}  
let employee: Employee = new Employee('Jane', 20);
```

In the last line of the code snippet above, we’re using `Employee` as the type of the instances of the class `Employee`.

Also, we’re creating another value we call a _constructor function._ This is the function that’s called when the `new` keyword is being used to create a new instance of the class.

If we compile the TypeScript code above to ES5 or earlier, we can see we get something like the following code generated:

```
"use strict";  
var Employee = /** @class (function () {  
    function Employee(name, age) {  
        this.name = name;  
        this.age = age;  
    }  
    return Employee;  
}());  
var employee = new Employee('Jane', 20);
```

In the code above, the constructor function is the following code:

```
function Employee(name, age) {  
  this.name = name;  
  this.age = age;  
}
```

We have this because in JavaScript, no matter what version we’re using, a class is just the syntactic sugar for constructor functions. The inheritance model of TypeScript just extends from JavaScript. It didn’t change the inheritance model of JavaScript since it’s supposed to be 100% compatible with existing JavaScript code so existing JavaScript code can be adopted to use TypeScript.

In TypeScript, we can get the type of the constructor function with the `typeof` operator. It’s different from its usage in JavaScript as it has been extended to get the type of the constructor function of a class. If we run the following code …

```
class Employee{  
  name: string;  
  age: number;  
  static companyName: string = 'ABC Company';  
  constructor(name: string, age: number) {  
    this.name = name;  
    this.age = age;  
  }}

let employeeConstructor: typeof Employee = Employee;  
console.log(Employee.companyName);  
console.log(employeeConstructor.companyName);
```

… it makes sense when we get the value of the static member `companyName` logged in both `console.log` statements. As we can see from the compiled output …

```
"use strict";  
var Employee = /** @class */ (function () {  
    function Employee(name, age) {  
        this.name = name;  
        this.age = age;  
    }  
    Employee.companyName = 'ABC Company';  
    return Employee;  
}());  
var employeeConstructor = Employee;  
console.log(Employee.companyName);  
console.log(employeeConstructor.companyName);
```

… a JavaScript and TypeScript class are ultimately just functions. The class syntax makes inheritance of JavaScript easier to use since it looks like it’s using class-based inheritance, but it’s actually syntactic sugar on top of the prototypical inheritance model that’s been the same since JavaScript first came out.

TypeScript makes inheritance easy by letting us define abstract classes — where some implementation is done by the abstract and others are done in the subclass that extends the abstract class.

We also have abstract methods that subclasses can implement. Abstract methods only have the signature and return type and no implementation details.

Static members let us define members that are part of the class rather than an instance of the class.

It’s also important to know that the class syntax is ultimately syntactic sugar for the prototypical inheritance model that existed since the beginning of JavaScript. However, now we can put that aside since we can use the syntactic sugar to make it easier to understand and implement inheritance.